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-map
component, which will display the map. -
Add an
id
to thearcgis-map
component 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="initial-scale=1, maximum-scale=1, user-scalable=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>
</script>
</html>
- In the
<head
element, add references to Calcite Components and ArcGIS Maps SDK for JavaScript.>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
<title>Calcite Components: Create a mapping app with web components</title>
<script src="https://js.arcgis.com/calcite-components/2.13.2/calcite.esm.js" type="module"></script>
<link rel="stylesheet" href="https://js.arcgis.com/calcite-components/2.13.2/calcite.css" />
<script src="https://js.arcgis.com/4.31/"></script>
<link rel="stylesheet" href="https://js.arcgis.com/4.31/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="initial-scale=1, maximum-scale=1, user-scalable=no" />
<title>Calcite Components: Create a mapping app with web components</title>
<script src="https://js.arcgis.com/calcite-components/2.13.2/calcite.esm.js" type="module"></script>
<link rel="stylesheet" href="https://js.arcgis.com/calcite-components/2.13.2/calcite.css" />
<script src="https://js.arcgis.com/4.31/"></script>
<link rel="stylesheet" href="https://js.arcgis.com/4.31/esri/themes/light/main.css" />
<script type="module" src="https://js.arcgis.com/map-components/4.31/arcgis-map-components.esm.js"></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 your developer dashboard to get an API key.
- Back in CodePen >
<script
, import the> esri
class.Config - Set the
api
property.Key
<script>
require(["esri/config"],
function (esriConfig) {
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-map
component using theitem-id
attribute, which specifies the webmap's item ID.
<arcgis-map id="mapEl" item-id="03d584a7c9874b44821c6a766c3bbc11">
</arcgis-map>
- Next, add an
arcgis-zoom
component and set itsposition
with respect to thearcgis-map
component.
<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-shell
component, and set thecontent-behind
attribute 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-panel
component, placing it in shell's"panel-end"
slot. -
Then, set the shell panel's
display
attribute 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-scale="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-panel
component. Supply anid
to dynamically populate theheading
with the title of the web map, and add thehidden
attribute to hide the component on initialization. -
Then, slot a
calcite-icon
into panel's"header-actions-start"
slot. -
Next, slot
calcite-action-bar
into panel's"action-bar"
slot, where you can addcalcite-actions
to display Map components. -
For each action, add a unique
data-action-id
value.
<calcite-shell-panel id="shell-panel-nav" slot="panel-end" display-mode="float" height-scale="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-block
component.- Add the
open
attribute to display its contents - Supply a unique
heading
- Provide the value from the corresponding
data-action-id
to the block'sdata-block-id
attribute - Add the
hidden
attribute so the component and its contents are not displayed when the app is initialized
- Add the
-
In the
calcite-block
, slot thearcgis-layer-list
Map component.- Set the
reference-element
to the map component'sid
- Set the
<calcite-shell-panel id="shell-panel-nav" slot="panel-end" display-mode="float" height-scale="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 open 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-block
components to contain thearcgis-basemap-gallery
,arcgis-legend
,arcgis-bookmarks
, andarcgis-print
components.
<!-- Map-specific blocks containing ArcGIS Maps SDK for JavaScript components -->
<calcite-block open 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 open heading="Basemaps" data-block-id="basemaps" hidden>
<arcgis-basemap-gallery reference-element="mapEl"></arcgis-basemap-gallery>
</calcite-block>
<calcite-block open heading="Legend" data-block-id="legend" hidden>
<arcgis-legend legend-style="classic" reference-element="mapEl"></arcgis-legend>
</calcite-block>
<calcite-block open heading="Bookmarks" data-block-id="bookmarks" hidden>
<arcgis-bookmarks editing-enabled="false" reference-element="mapEl"></arcgis-bookmarks>
</calcite-block>
<calcite-block open 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-block
component.- Add the
open
attribute to display its contents - Supply a unique
heading
- Provide the value from the corresponding
data-action-id
to the block'sdata-block-id
attribute - Add the
closed
attribute so the component and its contents are not displayed when the app is initialized
- Add the
-
Add the
calcite-card
component. -
Add the following child elements to the card, with unique
id
's:- Define an
img
in the card's"thumbnail"
slot - Add three
div
elements, 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 open 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-map
component in the<script
tag.>
<script>
const mapEl = document.getElementById("mapEl");
</script>
-
On the map's
arcgis
event, 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:
title
will supply the panel'sheading
and card's"heading"
slotthumbnail
adds the card'sUrl img
'ssrc
property to the"thumbnail"
slotsnippet
andmodified
supplement card's"description"
slottags
will iterate through each tag to create individualcalcite-chip
components 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-action
is clicked. The function will hide the activecalcite-block
and 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 open 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-bar
using 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
<body
element after the shell's closing tag, add a> calcite-loader
to display the component.
</calcite-shell>
<calcite-loader></calcite-loader>
- Inside of the
arcgis
event, below the rest of the JavaScript code, hide theView Ready Change calcite-loader
component with thehidden
property set totrue
and 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.querySelector("calcite-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-icon
slotted 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-panel
via the--calcite-shell-panel-min-width
style- A media query for
calcite-shell-panel
to support smaller devices, such as mobile phones calcite-block
components containing adata-block-id
attribute 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-card
by addingpadding
to the component, updating thefont-size
using 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 open and close the calcite-block
components, which contain the ArcGIS Maps SDK for JavaScript Map components.