Learn how to construct a UI for a mapping application with web components using Calcite Components and ArcGIS Maps SDK for JavaScript's Map components.
You will use Calcite Components to enhance the user experience and drive interaction in your mapping application. This tutorial focuses on the user interface and Map components to streamline app creation, and does not require prior knowledge of the ArcGIS Maps SDK for JavaScript. If you are new to the ArcGIS Maps SDK for JavaScript, you can also explore this great tutorial that covers similar mapping concepts used in this application.
Prerequisites
Steps
Create a new pen
- Navigate to CodePen to create a new pen for your mapping application.
Add HTML
-
In CodePen > HTML, add HTML and CSS to create a page with an
arcgis-mapcomponent, which will display the map. -
Add an
idto thearcgis-mapcomponent to reference throughout app development, add CSS to set it's width and height to the browser's window, and Flexbox to supplement further app styling.
The <! tag is not required in CodePen. If you are using a different editor or running the page on a local server, be sure to add this tag to the top of your HTML page.
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Calcite Components: Construct a mapping app with web components</title>
</head>
<style>
html,
body,
#mapEl {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
display: flex;
}
</style>
<body>
<arcgis-map id="mapEl"></arcgis-map>
</body>
<script type="module">
</script>
</html>- In the
<headelement, add references to Calcite Components and ArcGIS Maps SDK for JavaScript.>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Calcite Components: Create a mapping app with web components</title>
<script src="https://js.arcgis.com/calcite-components/3.3.3/calcite.esm.js" type="module"></script>
<script src="https://js.arcgis.com/4.33/"></script>
<link rel="stylesheet" href="https://js.arcgis.com/4.33/esri/themes/light/main.css" />
</head>
- Next, add the reference to the ArcGIS Maps SDK for JavaScript Map components.
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Calcite Components: Create a mapping app with web components</title>
<script src="https://js.arcgis.com/calcite-components/3.3.3/calcite.esm.js" type="module"></script>
<script src="https://js.arcgis.com/4.33/"></script>
<link rel="stylesheet" href="https://js.arcgis.com/4.33/esri/themes/light/main.css" />
<script type="module" src="https://js.arcgis.com/4.33/map-components"></script>
</head>
Use an API key
An API key is required to access ArcGIS services if you are using a developer account. You can skip this step if you have an account associated with an ArcGIS Online organization.
- Go to the get started with security and authentication to build an app that can access secure resources.
- Back in CodePen >
<script, import the> esriclass.Config - Set the
apiproperty.Key
<script type="module">
const [esriConfig] = await $arcgis.import([
"@arcgis/core/config.js"
]);
esriConfig.apiKey = "YOUR_API_KEY";
</script>Display a map
The map is the central focus of this application. You added CSS above which makes the map the full width and height of the window. You will also add ArcGIS Maps SDK for JavaScript Map components that interact with the map.
- In the
<body, you will initialize the> arcgis-mapcomponent using theitem-idattribute, which specifies the webmap's item ID.
<arcgis-map id="mapEl" item-id="03d584a7c9874b44821c6a766c3bbc11">
</arcgis-map>
- Next, add an
arcgis-zoomcomponent and set itspositionwith respect to thearcgis-mapcomponent.
<arcgis-map id="mapEl" item-id="03d584a7c9874b44821c6a766c3bbc11">
<arcgis-zoom position="top-left"></arcgis-zoom>
</arcgis-map>
At this point the application will display a map. Next, you'll use Calcite Components to build the app's user interface.
Create the layout
To create the layout you will use calcite-shell, which organizes other components on the page using slots. Slots are a web component concept and there is a brief section in Core concepts. A list of Calcite Components slots, if it has any, can be found on its reference page. For example, here are shell's slots.
- Add the
calcite-shellcomponent, and set thecontent-behindattribute so users can interact with the map behind the shell.
<calcite-shell content-behind>
<arcgis-map id="mapEl" item-id="03d584a7c9874b44821c6a766c3bbc11">
<arcgis-zoom position="top-left"></arcgis-zoom>
</arcgis-map>
</calcite-shell>
-
Next, add the
calcite-shell-panelcomponent, placing it in shell's"panel-end"slot. -
Then, set the shell panel's
displayattribute toMode "float"so it's content appears to hover over the map.
<calcite-shell content-behind>
<calcite-shell-panel id="shell-panel-nav" slot="panel-end" display-mode="float" height="l">
</calcite-shell-panel>
<arcgis-map id="mapEl" item-id="03d584a7c9874b44821c6a766c3bbc11">
<arcgis-zoom position="top-left"></arcgis-zoom>
</arcgis-map>
</calcite-shell>
-
Add a
calcite-panelcomponent. Supply anidto dynamically populate theheadingwith the title of the web map, and add thehiddenattribute to hide the component on initialization. -
Then, slot a
calcite-iconinto panel's"header-actions-start"slot. -
Next, slot
calcite-action-barinto panel's"action-bar"slot, where you can addcalcite-actionsto display Map components. -
For each action, add a unique
data-action-idvalue.
<calcite-shell-panel id="shell-panel-nav" slot="panel-end" display-mode="float" height="l">
<calcite-panel id="app-heading" hidden>
<calcite-icon id="logo-icon" slot="header-actions-start" icon="explore" text-label="explore"></calcite-icon>
<calcite-action-bar id="map-actions" slot="action-bar" layout="horizontal" expand-disabled>
<calcite-action icon="layers" text="Layers" data-action-id="layers"></calcite-action>
<calcite-action icon="basemap" text="Basemaps" data-action-id="basemaps"></calcite-action>
<calcite-action icon="legend" text="Legend" data-action-id="legend"></calcite-action>
<calcite-action icon="bookmark" text="Bookmarks" data-action-id="bookmarks"></calcite-action>
<calcite-action icon="print" text="Print" data-action-id="print"></calcite-action>
<calcite-action icon="information" text="About" data-action-id="information"></calcite-action>
</calcite-action-bar>
</calcite-panel>
</calcite-shell-panel>
Add Map components
To build on your layout, you will add Map components that display when a user interacts with a calcite-action.
-
First, add a
calcite-blockcomponent.- Add the
expandedattribute to display its contents - Supply a unique
heading - Provide the value from the corresponding
data-action-idto the block'sdata-block-idattribute - Add the
hiddenattribute so the component and its contents are not displayed when the app is initialized
- Add the
-
In the
calcite-block, slot thearcgis-layer-listMap component.- Set the
reference-elementto the map component'sid
- Set the
<calcite-shell-panel id="shell-panel-nav" slot="panel-end" display-mode="float" height="l">
<calcite-panel id="app-heading" hidden>
<calcite-icon id="logo-icon" slot="header-actions-start" icon="explore" text-label="explore"></calcite-icon>
<calcite-action-bar id="map-actions" slot="action-bar" layout="horizontal" expand-disabled>
<calcite-action icon="layers" text="Layers" data-action-id="layers"></calcite-action>
<calcite-action icon="basemap" text="Basemaps" data-action-id="basemaps"></calcite-action>
<calcite-action icon="legend" text="Legend" data-action-id="legend"></calcite-action>
<calcite-action icon="bookmark" text="Bookmarks" data-action-id="bookmarks"></calcite-action>
<calcite-action icon="print" text="Print" data-action-id="print"></calcite-action>
<calcite-action icon="information" text="About" data-action-id="information"></calcite-action>
</calcite-action-bar>
</calcite-panel>
<!-- Map-specific blocks containing ArcGIS Maps SDK for JavaScript components -->
<calcite-block expanded heading="Layers" data-block-id="layers" hidden>
<arcgis-layer-list drag-enabled reference-element="mapEl" visibility-appearance="checkbox"></arcgis-layer-list>
</calcite-block>
</calcite-shell-panel>
- Repeat steps 1 and 2 to add
calcite-blockcomponents to contain thearcgis-basemap-gallery,arcgis-legend,arcgis-bookmarks, andarcgis-printcomponents.
<!-- Map-specific blocks containing ArcGIS Maps SDK for JavaScript components -->
<calcite-block expanded heading="Layers" data-block-id="layers" hidden>
<arcgis-layer-list drag-enabled reference-element="mapEl" visibility-appearance="checkbox"></arcgis-layer-list>
</calcite-block>
<calcite-block expanded heading="Basemaps" data-block-id="basemaps" hidden>
<arcgis-basemap-gallery reference-element="mapEl"></arcgis-basemap-gallery>
</calcite-block>
<calcite-block expanded heading="Legend" data-block-id="legend" hidden>
<arcgis-legend legend-style="classic" reference-element="mapEl"></arcgis-legend>
</calcite-block>
<calcite-block expanded heading="Bookmarks" data-block-id="bookmarks" hidden>
<arcgis-bookmarks editing-enabled="false" reference-element="mapEl"></arcgis-bookmarks>
</calcite-block>
<calcite-block expanded heading="Print" data-block-id="print" hidden>
<arcgis-print allowed-formats="all" allowed-layouts="all" include-default-templates="false"
reference-element="mapEl"></arcgis-print>
</calcite-block>
Create an information card
An information card can provide users with information about the app's data. You will use calcite-card to display the webmap's information, where the card will dynamically populate and organize the title, thumbnail image, description, last modified date, and tags.
-
Similar to the other Map components, add a
calcite-blockcomponent.- Add the
expandedattribute to display its contents - Supply a unique
heading - Provide the value from the corresponding
data-action-idto the block'sdata-block-idattribute - Add the
closedattribute so the component and its contents are not displayed when the app is initialized
- Add the
-
Add the
calcite-cardcomponent. -
Add the following child elements to the card, with unique
id's:- Define an
imgin the card's"thumbnail"slot - Add three
divelements, one in the"heading"slot, another in the"description"slot, and finally one in the"footer-end"slot
- Define an
<!-- Info block (populates with info from the web map) -->
<calcite-block expanded heading="About the data" data-block-id="information" hidden>
<calcite-card id="card-content">
<img id="card-thumbnail" alt="Webmap thumbnail" slot="thumbnail" />
<div id="card-heading" slot="heading">
<!-- Dynamically populated -->
</div>
<div id="card-description" slot="description">
<!-- Dynamically populated -->
</div>
<div id="card-tags" slot="footer-end">
<!-- Dynamically populated -->
</div>
</calcite-card>
</calcite-block>
Populate the content
You finished adding components to your application! Now populate the panel's heading and information card with content from the web map.
- Declare the
arcgis-mapcomponent in the<scripttag.>
<script type="module">
const mapEl = document.getElementById("mapEl");
</script>
-
On the map's
arcgisevent, declare constants for the followingView Ready Change portal's to populate contents from the web map:Item title,thumbnail,Url snippet,modified, andtags. -
Next, add the values to the UI, where:
titlewill supply the panel'sheadingand card's"heading"slotthumbnailadds the card'sUrl img'ssrcproperty to the"thumbnail"slotsnippetandmodifiedsupplement card's"description"slottagswill iterate through each tag to create individualcalcite-chipcomponents in card's"footer-end"slot
mapEl.addEventListener("arcgisViewReadyChange", () => {
const { title, thumbnailUrl, snippet, modified, tags } = mapEl.map.portalItem;
document.getElementById("app-heading").heading = `${title} Explorer`;
document.getElementById("card-heading").innerHTML = title;
document.getElementById("card-thumbnail").src = thumbnailUrl;
document.getElementById("card-description").innerHTML = `<p>${snippet}</p><p>Last modified on ${modified}.</p>`;
tags.forEach(tag => {
document.getElementById("card-tags").innerHTML += `<calcite-chip>${tag}</calcite-chip>`;
});
});
Make components interactive
The next step is to display the calcite-block components, which contain the Map components, when clicking on the corresponding calcite-action components.
- Create a function that will execute when each
calcite-actionis clicked. The function will hide the activecalcite-blockand display the block corresponding to the clicked action. If the user clicks on the active action, the corresponding block will close and there will not be any expanded blocks.
This step uses attribute selectors to access the action and block elements using the data attributes you added above. The values of the data attributes are the names of the corresponding Map component.
- Create a click event listener on the
calcite-action-barusing the function above as the callback.
let activeWidget;
const handleActionBarClick = ({ target }) => {
if (target.tagName !== "CALCITE-ACTION") {
return;
}
if (activeWidget) {
document.querySelector(`[data-action-id=${activeWidget}]`).active = false;
document.querySelector(`[data-block-id=${activeWidget}]`).hidden = true;
}
const nextWidget = target.dataset.actionId;
if (nextWidget !== activeWidget) {
document.querySelector(`[data-action-id=${nextWidget}]`).active = true;
document.querySelector(`[data-block-id=${nextWidget}]`).hidden = false;
activeWidget = nextWidget;
} else {
activeWidget = null;
}
};
document.querySelector("calcite-action-bar").addEventListener("click", handleActionBarClick);
Add a loader component
Now everything is interactive in your application! You can open and close Map components using Calcite Components. However, the application takes a second to load, which should be communicated to the user.
- In the
<bodyelement after the shell's closing tag, add a> calcite-loaderto display the component.
</calcite-shell>
<calcite-loader></calcite-loader>- Inside of the
arcgisevent, below the rest of the JavaScript code, hide theView Ready Change calcite-loadercomponent with thehiddenproperty set totrueand display thecalcite-panel. When the map and its properties have loaded into the app, the loader will be hidden and the panel will display in the UI.
mapEl.addEventListener("arcgisViewReadyChange", () => {
const { title, thumbnailUrl, snippet, modified, tags } = mapEl.map.portalItem;
document.getElementById("app-heading").heading = `${title} Explorer`;
document.getElementById("card-heading").innerHTML = title;
document.getElementById("card-thumbnail").src = thumbnailUrl;
document.getElementById("card-description").innerHTML = `<p>${snippet}</p><p>Last modified on ${modified}.</p>`;
tags.forEach(tag => {
document.getElementById("card-tags").innerHTML += `<calcite-chip>${tag}</calcite-chip>`;
});
document.getElementById("app-loader").hidden = true;
document.getElementById("app-heading").removeAttribute("hidden");
});
Add styling
In the <style element, add some additional CSS to enhance the user interface for your heading calcite-panel and information calcite-card.
-
Add Flexbox variables to the
calcite-iconslotted into the panel's"header-actions-start"to align the component. -
Next, add styles for the panel to set the width and heights for:
calcite-shell-panelvia the--calcite-shell-panel-min-widthstyle- A media query for
calcite-shell-panelto support smaller devices, such as mobile phones calcite-blockcomponents containing adata-block-idattribute withmax-height
#logo-icon {
align-self: center;
height: auto;
margin-inline-start: 0.5rem;
}
#shell-panel-nav {
--calcite-shell-panel-min-width: 18rem;
}
@media (max-width: 400px) {
#shell-panel-nav {
--calcite-shell-panel-min-width: 80vw;
}
}
calcite-block[data-block-id] {
max-height: 50vh;
overflow-y: auto;
}
- Finally, add styles to
calcite-cardby addingpaddingto the component, updating thefont-sizeusing CSS variables, and wrapping the tags withflex-wrap.
#card-heading {
font-size: var(--calcite-font-size-md);
}
#card-description {
font-size: var(--calcite-font-size);
}
#card-tags {
flex-wrap: wrap;
}
Run the app
In CodePen, run your code to display the application.
The map will display once the application finishes loading, along with the web map's title and a calcite-action-bar. Clicking on the calcite-action components will expand and collapse the calcite-block components, which contain the ArcGIS Maps SDK for JavaScript Map components.