これまで Calcite Design System の使用方法として、Calcite Design System のチュートリアルを3回連載しました。本記事では第4回目として、チュートリアルの Filter recent earthquakes を紹介します。
第1回:地図アプリの作成
第2回:ダークモードスイッチの作成
第3回:コアコンセプトの適用
第4回:近発生した地震のフィルタリング ← 本記事
Calcite Design System を使用してユーザー インター フェイス (UI) を構築します。このチュートリアルでは、複雑な問題を解決するために複製や拡張が可能な Calcite Components の概要を説明します。
ここでは、以下のことを行います。
・フィーチャ サービスから属性にアクセスするためのクエリの実行方法
・クエリで見つかったレコードの数を動的に入力するための calcite-notice を追加
・クエリから個々のレコードを表示するために calcite-card を動的に追加
・Map Viewer のレコードの位置にリンクする calcite-button の作成
このチュートリアルでは vanilla JavaScript を使用していますが、これらの概念と対話の方法はフレームワークに関係なく適用できます。地図を含むチュートリアルは「地図アプリを作成する」をご覧ください。
ArcGIS 開発者アカウント
このチュートリアルで使用するサービスにアクセスするには、無料の ArcGIS 開発者アカウントまたは ArcGIS Online 組織に関連付けられたアカウントが必要です。
新しいペンの作成
1.CodePen にアクセスし、アプリケーション用の新しいペンを作成します。
HTML の追加
1.CodePen > HTML で、HTMLを追加して <body> タグのあるページを作成します。また、<main> セクシ ョンを追加します。CodePenでは、<!DOCTYPE html> タグは必要ありません。別のエディターを使用している場合や、ローカル サーバーでページを実行している場合は、必ずこのタグを HTML ページの先頭に追加してください。
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Calcite Components: Filter recent earthquakes</title>
</head>
<style>
</style>
<body>
<main>
</main>
</body>
<script>
</script>
</html>
2.Calcite Design System Web コンポーネントにアクセスするには、<head> 要素に Calcite Components への参照を追加します。
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Calcite Components: Filter recent earthquakes</title>
<script src="https://js.arcgis.com/calcite-components/1.0.7/calcite.esm.js" type="module"></script>
<link rel="stylesheet" href="https://js.arcgis.com/calcite-components/1.0.7/calcite.css" />
</head>
3.次に、アプリケーションの中身を整理します。main セクションに、地震の結果を格納するために、heading と heading-level の属性を持つ calcite-shell と calcite-panel を追加します。
headingとheading-level 属性は、より多くのユーザーをサポートするために、アプリケーション全体のページ階層 (page hierarchy) を配置します。これはページの最初のヘッダーなので、heading-level 値を "1 " に設定します。
<calcite-shell>
<!-- Panel to display records -->
<calcite-panel heading="Earthquake results" heading-level="1" description="Search by location to display results">
</calcite-panel>
</calcite-shell>
レコードの表示
ユーザーが調整できるように、HTMLコンテンツの構築を続けます。コンテンツは、ユーザーがアプリケーションの操作ることで変化します。
1.calcite-panel に calcite-filter を追加します。この calcite-filter は地震のフィーチャ サービスへのクエリに使用される予定です。
<!-- Filter records -->
<calcite-filter placeholder="Try searching Alaska"></calcite-filter>
2.次に id 属性を持つ calcite-notice を追加し、ユーザーにオンデマンドでフィードバックを提供します。
<!-- An active class will be added to display the number of records -->
<calcite-notice id="note">
</calcite-notice>
3.calcite-notice では、divを追加して title スロットに配置し、後で id 属性を使って地震発生件数を入力するのに使用します。
<!-- An active class will be added to display the number of records -->
<calcite-notice id="note">
<div id="number-records" slot="title">
<!-- Content is automatically generated -->
</div>
</calcite-notice>
4.地震の結果を格納するために、class 属性を持つ div を追加します。また、パネルの footer スロットに calcite-pagination を配置し、page-size 属性を追加して1ページに表示する件数を指定します。
<!-- Container with Cards -->
<div class="card-container">
<!-- Content is automatically generated -->
</div>
<!-- Pagination -->
<calcite-pagination slot="footer" page-size="12" style="visibility:hidden" >
</calcite-pagination>
データのクエリ
ユーザーが入力した検索キーワードで地震のフィーチャ サービスを取得する JavaScript 機能を追加します。
1.まず、後で要素を参照するために calcite-filter、calcite-pagination、calcite-notice、card-container の CSS クラスを参照する定数と変数を作成します。
const filterElement = document.querySelector("calcite-filter");
const paginationElement = document.querySelector("calcite-pagination");
const noticeElement = document.querySelector("calcite-notice");
const cardContainer = document.querySelector(".card-container");
2.次に、Fetch API を使用して earthquakes サービスに問い合わせます。成功したら、Response.json() でレスポンスを解析し、map() メソッドでフィーチャを取得します。
/* Fetch the earthquakes feature service */
fetch("https://services9.arcgis.com/RHVPKKiFTONKtxq3/ArcGIS/rest/services/USGS_Seismic_Data_v1/FeatureServer/0/query?where=1%3D1&outFields=*&f=json")
.then((response) => response.json())
.then(({features}) => features.map(({attributes}) => attributes))
結果のフィルタリング
次に、解析されたレスポンスを使用して、ユーザー定義の結果をフィルタリングして表示します。
1.initFilter という named function expression を使用して、結果をフィルタリングします。上記のステップで定義した定数と変数を使用して calcite-filter コンポーネントの items プロパティを設定します。
/* Fetch the earthquakes feature service */
fetch("https://services9.arcgis.com/RHVPKKiFTONKtxq3/ArcGIS/rest/services/USGS_Seismic_Data_v1/FeatureServer/0/query?where=1%3D1&outFields=*&f=json")
.then((response) => response.json())
.then(({features}) => features.map(({attributes}) => attributes))
.then((attributes) => initFilter(attributes));
/* Filter the results to display */
const initFilter = (items) => {
filterElement.items = items;
};
2.initFilterで、calciteFilterChange イベントのリスナーを追加して、フィルタの value の変化を監視します。次にpaginationの startItem と totalItems のプロパティを変更して、最初のページに戻ります。最後に、フィルタの value が falsey でないときにカードを追加する条件を作成します。
フィルタの [`filteredItems`](xref://site.self/filter/#component-api-properties-filteredItems) プロパティには、[`value`](xref://site.self/filter/#component-api-properties-value)がない場合にすべての項目が格納されます。検索機能を作るには、ユーザーがクエリ用語を入力したときだけ、カードを追加する必要があります。
/* Filter the results to display */
const initFilter = (items) => {
filterElement.items = items;
document.addEventListener("calciteFilterChange", () => {
paginationElement.startItem = 1;
paginationElement.totalItems = 0;
// When a Filter value is present
// Create Cards, update Pagination, and number of responses
if (filterElement.value) {
}
});
};
3.次にcalciteFilterChange 内で、フィルタリングされた地震の totalItems が指定した pageSize 値よりも大きい場合に calcite-pagination コンポーネントを表示します。
calcite-pagination コンポーネントの CSS プロパティの visibility を "hidden" に設定します。結果が 1 ページ以上ある場合は、CSS プロパティの値を ”visible” に変更します。
/* Filter the results to display */
const initFilter = (items) => {
filterElement.items = items;
document.addEventListener("calciteFilterChange", () => {
paginationElement.startItem = 1;
paginationElement.totalItems = 0;
paginationElement.style.visibility = "hidden";
// When a Filter value is present
// Create Cards, update Pagination, and number of responses
if (filterElement.value) {
// If additional pages are populated, display Pagination
if (paginationElement.total > paginationElement.pageSize) {
paginationElement.style.visibility = "visible";
}
}
});
};
地震を表示
地震を表示するために、各結果の属性を calcite-card に格納します。また、calcite-button を追加し、アクセスすると、地震の位置が Map Viewer で表示されるようにします。
1.フィルタリングされた地震を、card-container クラスに属する calcite-card コンポーネントに、createCard という名前の関数式で配置します。
/* Create Cards and their content */
const createCard = (item) => {
const titleName = item.place.replace(/[;']/g,"");
// Populate Card content
if (cardContainer.childElementCount < paginationElement.pageSize) {
const cardString =
`<calcite-card id="card-${item.OBJECTID}">
<span slot="title">
<b>${item.place}</b>
</span>
<span slot="subtitle">
Occurred on: ${new Date(item.eventTime)}
</span>
<calcite-chip
appearance="outline-fill"
scale="s"
kind="inverse"
icon="graph-time-series"
>
Magnitude: ${item.mag}
</calcite-chip>
<calcite-button
label="Open ${titleName} in map"
icon-end="launch"
slot="footer-end"
target="_blank"
width="full"
href="https://www.arcgis.com/apps/mapviewer/index.html?` +
`marker=${item.longitude};${item.latitude};` + // Marker (lng, lat)
`4326;` + // Coordinate system
titleName + `;` +
`https://clipart.world/wp-content/uploads/2021/07/Target-clipart-transparent.png;` + // Marker image
`Magnitude: ${item.mag}&` +
`level=6"
>
Open in map
</calcite-button>
</calcite-card>`;
const cardElement = document
.createRange()
.createContextualFragment(cardString);
cardContainer.appendChild(cardElement);
}
};
2.カードを作成するには、フィルタ value に一致する各フィーチャの calciteFilterChange イベントリスナーで createCard を呼び出します。結果を含む cardContainer の内容をクリアします。
/* Filter the results to display */
const initFilter = (items) => {
filterElement.items = items;
document.addEventListener("calciteFilterChange", () => {
paginationElement.startItem = 1;
paginationElement.totalItems = 0;
paginationElement.style.visibility = "hidden";
cardContainer.innerHTML = "";
// When a Filter value is present
// Create Cards, update Pagination, and number of responses
if (filterElement.value) {
filterElement.filteredItems.forEach((item) => createCard(item));
paginationElement.totalItems = filterElement.filteredItems.length;
// If additional pages are populated, display Pagination
if (paginationElement.totalItems > paginationElement.pageSize) {
paginationElement.style.visibility = "visible";
}
}
});
};
3.次に、ユーザーに対して地震の結果件数を表示します。関数 showNumberOfResponses を作成し、calcite-notice のタイトルにレスポンス件数を転記します。ユーザーがコンテンツにアクセスできるように、Notice の open 属性を "true" に設定します。レスポンス数に応じて innerHTML、kind、icon 属性が変更されます。
/* Display the number of responses in a Notice */
function showNumberOfResponses(responseNumber) {
const note = document.getElementById("note");
const numberRecordsNote = document.getElementById("number-records");
// If 0 responses, add "Sorry" to the Notice text
// Add the Notice color and icon
if (responseNumber === 0) {
responseNumber = `Sorry, ${responseNumber}`
note.kind = "danger";
note.icon = "exclamation-mark-triangle";
} else {
note.kind = "brand";
note.icon = "information";
}
// Notice text
numberRecordsNote.innerHTML = `${responseNumber} records found.`;
noticeElement.open = true;
}
4.カードの作成と同様に、calciteFilterChange イベントリスナーのコールバック関数にshowNumberOfResponses 関数の呼び出しを追加します。フィルタリングされた値が存在しない場合、通知の open 属性を「false」に、calcite-pagination の visibility を「hidden」に設定します。
/* Filter the results to display */
const initFilter = (items) => {
filterElement.items = items;
document.addEventListener("calciteFilterChange", () => {
paginationElement.startItem = 1;
paginationElement.totalItems = 0;
// Prevent display of elements if no Filter value is present
noticeElement.open = false;
paginationElement.style.visibility = "hidden";
cardContainer.innerHTML = "";
// When a Filter value is present
// Create Cards, update Pagination, and number of responses
if (filterElement.value) {
filterElement.filteredItems.forEach((item) => createCard(item));
paginationElement.totalItems = filterElement.filteredItems.length;
showNumberOfResponses(filterElement.filteredItems.length);
// If additional pages are populated, display Pagination
if (paginationElement.totalItems > paginationElement.pageSize) {
paginationElement.style.visibility = "visible";
}
}
});
};
5.pagination の calcitePaginationChange イベントにリスナーを追加し、ユーザーがページを変更するときに後続のフィルタリング アイテムを表示できるようにします。filteredItems のサブセットを pagination の startItemプロパティから始まり、startItem プロパティと pageSize プロパティの合計で終わるように表示します。
/* Update Cards when interacting with Pagination */
document.addEventListener("calcitePaginationChange", ({ target }) => {
const displayItems = filterElement.filteredItems.slice(
target.startItem - 1,
target.startItem - 1 + target.pageSize
);
});
6.最後に、calcite-pagination を操作する際に、カードを更新します。cardContainer の内容を以前のフィルタリング結果でクリアし、calcitePaginationChange イベントリスナーで createCard を呼び出します。
/* Update Cards when interacting with Pagination */
document.addEventListener("calcitePaginationChange", ({ target }) => {
cardContainer.innerHTML = "";
const displayItems = filterElement.filteredItems.slice(
target.startItem - 1,
target.startItem - 1 + target.pageSize
);
displayItems.forEach((item) => createCard(item));
});
スタイリングの追加
これでアプリケーションの機能は完成です。CSS のスタイリングを使って、インターフェイスのデザインを最終調整します。
1.card-container クラスに、CSS grid layout、calcite-chip の震度表示色、calcite-notice の位置指定を用いたスタイルを追加します。
<style>
.card-container {
margin: 0.75rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-gap: 1rem;
justify-content: space-evenly;
}
calcite-chip {
color: var(--calcite-ui-danger);
}
calcite-notice {
position: relative;
margin: 0.75rem;
}
</style>
アプリを実行する
CodePenで、コードを実行すると、アプリケーションが表示されます。
今回は、Calcite Design System を使用したチュートリアルの Filter recent earthquakes のサンプルを翻訳して紹介しました。ArcGIS 開発リソース集にも Calcite Design System の開発に役立つガイド集を公開していますので、ご活用ください。
Calcite Design System チュートリアル集
・Tutorials | Calcite Design System | ArcGIS Developers
ArcGIS Developers
ArcGIS 開発リソース集