arcgis-tables-forms
ArcGIS Tables & Forms
Use this skill for configuring FeatureTable components/widgets and FormTemplate with various input elements.
Import Patterns
Direct ESM Imports
import FeatureTable from "@arcgis/core/widgets/FeatureTable.js";
import FeatureForm from "@arcgis/core/widgets/FeatureForm.js";
import FormTemplate from "@arcgis/core/form/FormTemplate.js";
import FieldColumnTemplate from "@arcgis/core/widgets/FeatureTable/support/FieldColumnTemplate.js";
Dynamic Imports (CDN)
const FeatureTable = await $arcgis.import(
"@arcgis/core/widgets/FeatureTable.js",
);
const FeatureForm = await $arcgis.import("@arcgis/core/widgets/FeatureForm.js");
const FormTemplate = await $arcgis.import("@arcgis/core/form/FormTemplate.js");
const FieldColumnTemplate = await $arcgis.import(
"@arcgis/core/widgets/FeatureTable/support/FieldColumnTemplate.js",
);
Note: The examples in this skill use Direct ESM imports. For CDN usage, replace
import X from "path"withconst X = await $arcgis.import("path").
FeatureTable Component
Basic Usage
<arcgis-map item-id="YOUR_WEBMAP_ID">
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
<arcgis-feature-table reference-element="arcgis-map"></arcgis-feature-table>
<script type="module">
const map = document.querySelector("arcgis-map");
const table = document.querySelector("arcgis-feature-table");
const view = await map.view;
await view.when();
const layer = view.map.layers.find((l) => l.type === "feature");
table.layer = layer;
</script>
Key Properties
| Property | Type | Default | Description |
|---|---|---|---|
layer |
FeatureLayer | The layer to display | |
attribute-table-template |
AttributeTableTemplate | Table configuration (new in 5.0) | |
editing-enabled |
boolean | false | Enable inline editing |
filter-by-selection-enabled |
boolean | false | Filter by selected features |
highlight-disabled |
boolean | false | Disable row highlighting |
hide-header |
boolean | false | Hide the table header |
hide-menu |
boolean | false | Hide the table menu |
hide-selection-column |
boolean | false | Hide the selection column |
multiple-selection-disabled |
boolean | false | Disable multi-row selection |
multiple-sort-enabled |
boolean | false | Enable multi-column sorting |
page-size |
number | Rows per page | |
auto-save-disabled |
boolean | false | Disable auto-saving edits |
definition-expression |
string | Filter expression | |
time-zone |
string | Time zone for dates |
Hide Menu Items
| Property | Description |
|---|---|
hide-menu-items-clear-selection |
Hide clear selection menu item |
hide-menu-items-delete-selection |
Hide delete selection menu item |
hide-menu-items-export-selection-to-csv |
Hide export to CSV menu item |
hide-menu-items-refresh-data |
Hide refresh data menu item |
hide-menu-items-toggle-columns |
Hide toggle columns menu item |
hide-menu-items-zoom-to-selection |
Hide zoom to selection menu item |
Key Events
| Event | Description |
|---|---|
arcgisSelectionChange |
Fires when row selection changes |
arcgisCellClick |
Fires when a cell is clicked |
arcgisCellKeydown |
Fires on keyboard interaction in a cell |
arcgisCellPointerover |
Fires when pointer enters a cell |
arcgisCellPointerout |
Fires when pointer leaves a cell |
arcgisColumnReorder |
Fires when columns are reordered |
arcgisPropertyChange |
Fires when a property changes |
arcgisReady |
Fires when the component is ready |
Component with Configuration
<arcgis-feature-table
reference-element="arcgis-map"
editing-enabled
multiple-sort-enabled
page-size="50"
hide-menu-items-delete-selection
></arcgis-feature-table>
<script type="module">
const table = document.querySelector("arcgis-feature-table");
table.layer = featureLayer;
table.addEventListener("arcgisSelectionChange", (event) => {
console.log("Selection changed");
});
</script>
FeatureTable Widget (Core API)
Basic Widget
import FeatureTable from "@arcgis/core/widgets/FeatureTable.js";
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
container: "tableDiv",
});
Configured Widget
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
container: "tableDiv",
editingEnabled: true,
multiSortEnabled: true,
highlightEnabled: true,
attachmentsEnabled: true,
relatedRecordsEnabled: true,
pageSize: 50,
filterGeometry: view.extent,
});
Column Templates
import FieldColumnTemplate from "@arcgis/core/widgets/FeatureTable/support/FieldColumnTemplate.js";
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
tableTemplate: {
columnTemplates: [
new FieldColumnTemplate({
fieldName: "name",
label: "Name",
initialSortPriority: 0,
direction: "asc",
}),
new FieldColumnTemplate({
fieldName: "status",
label: "Status",
}),
new FieldColumnTemplate({
fieldName: "value",
label: "Value ($)",
textAlign: "right",
}),
],
},
});
FieldColumnTemplate Properties
| Property | Type | Description |
|---|---|---|
fieldName |
string | Field to display |
label |
string | Column header text |
editable |
boolean | Allow editing |
visible |
boolean | Column visibility |
frozen |
boolean | Freeze column to left |
frozenToEnd |
boolean | Freeze column to right |
resizable |
boolean | Allow column resizing |
autoWidth |
boolean | Auto-size column width |
width |
number | Column width in pixels |
textAlign |
string | Text alignment |
textWrap |
boolean | Wrap text in cells |
initialSortPriority |
number | Sort priority |
Group Column Template
import GroupColumnTemplate from "@arcgis/core/widgets/FeatureTable/support/GroupColumnTemplate.js";
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
tableTemplate: {
columnTemplates: [
new GroupColumnTemplate({
label: "Location",
columnTemplates: [
new FieldColumnTemplate({ fieldName: "city", label: "City" }),
new FieldColumnTemplate({ fieldName: "state", label: "State" }),
new FieldColumnTemplate({ fieldName: "country", label: "Country" }),
],
}),
new GroupColumnTemplate({
label: "Details",
columnTemplates: [
new FieldColumnTemplate({ fieldName: "name", label: "Name" }),
new FieldColumnTemplate({ fieldName: "type", label: "Type" }),
],
}),
],
},
});
AttributeTableTemplate (New in 5.0)
Configure table display using AttributeTableTemplate, which can be set on a layer or on the feature table component.
import AttributeTableTemplate from "@arcgis/core/tables/AttributeTableTemplate.js";
const tableTemplate = new AttributeTableTemplate({
elements: [
{
type: "field",
fieldName: "name",
label: "Name",
},
{
type: "field",
fieldName: "status",
label: "Status",
},
{
type: "group",
label: "Location",
elements: [
{ type: "field", fieldName: "city", label: "City" },
{ type: "field", fieldName: "state", label: "State" },
],
},
],
orderByFields: [{ field: "name", order: "asc" }],
});
// Set on the feature table component
const table = document.querySelector("arcgis-feature-table");
table.attributeTableTemplate = tableTemplate;
// Or set on the layer
featureLayer.attributeTableTemplate = tableTemplate;
AttributeTableTemplate Element Types
| Type | Class | Description |
|---|---|---|
field |
AttributeTableFieldElement | Individual field column |
group |
AttributeTableGroupElement | Group of columns |
relationship |
AttributeTableRelationshipElement | Related records |
attachment |
AttributeTableAttachmentElement | Attachment column |
Selection and Highlighting
Programmatic Selection
// Select by ObjectIDs
featureTable.highlightIds.add(123);
featureTable.highlightIds.addMany([124, 125, 126]);
// Clear selection
featureTable.highlightIds.removeAll();
// Select from query
const results = await featureLayer.queryObjectIds({
where: "status = 'active'",
});
featureTable.highlightIds.addMany(results);
Watch Selection Changes
featureTable.highlightIds.on("change", (event) => {
console.log("Added IDs:", event.added);
console.log("Removed IDs:", event.removed);
const selectedIds = featureTable.highlightIds.toArray();
console.log("All selected:", selectedIds);
});
Sync with Map Selection
// Map click selects in table
view.on("click", async (event) => {
const response = await view.hitTest(event);
const feature = response.results.find(
(r) => r.graphic.layer === featureLayer,
);
if (feature) {
featureTable.highlightIds.removeAll();
featureTable.highlightIds.add(feature.graphic.attributes.OBJECTID);
}
});
// Table selection highlights on map
let highlightHandle;
featureTable.highlightIds.on("change", async () => {
const layerView = await view.whenLayerView(featureLayer);
if (highlightHandle) {
highlightHandle.remove();
}
const objectIds = featureTable.highlightIds.toArray();
if (objectIds.length > 0) {
highlightHandle = layerView.highlight(objectIds);
}
});
Filter and Refresh
// Filter by geometry
featureTable.filterGeometry = view.extent;
// Filter by expression
featureTable.layer.definitionExpression = "category = 'A'";
// Refresh data
featureTable.refresh();
// Clear filters
featureTable.filterGeometry = null;
featureTable.layer.definitionExpression = null;
FormTemplate
Configure edit forms for features.
Basic FormTemplate
import FormTemplate from "@arcgis/core/form/FormTemplate.js";
const formTemplate = new FormTemplate({
title: "Edit Feature",
description: "Update the feature attributes",
elements: [
{
type: "field",
fieldName: "name",
label: "Name",
},
{
type: "field",
fieldName: "category",
label: "Category",
},
{
type: "field",
fieldName: "description",
label: "Description",
},
],
});
featureLayer.formTemplate = formTemplate;
FormTemplate Properties
| Property | Type | Description |
|---|---|---|
title |
string | Form title |
description |
string | Form description |
elements |
FormElement[] | Array of form elements |
expressionInfos |
ExpressionInfo[] | Arcade expression definitions |
preserveFieldValuesWhenHidden |
boolean | Keep hidden field values |
Form Element Types
| Type | Description |
|---|---|
field |
FieldElement - Edit a specific field |
group |
GroupElement - Group of elements |
text |
TextElement - Static text/HTML content |
relationship |
RelationshipElement - Related records |
attachment |
AttachmentElement - File attachments |
Field Elements
{
type: "field",
fieldName: "name",
label: "Name",
description: "Enter the feature name",
hint: "Required field",
requiredExpression: "always-required",
editableExpression: "$feature.status != 'locked'",
visibilityExpression: "$feature.type != 'hidden'"
}
Group Elements
{
type: "group",
label: "Location",
description: "Address information",
initialState: "collapsed", // expanded, collapsed
elements: [
{ type: "field", fieldName: "address", label: "Address" },
{ type: "field", fieldName: "city", label: "City" },
{ type: "field", fieldName: "state", label: "State" }
]
}
Text Elements
{
type: "text",
text: "<h3>Important Instructions</h3><p>Please fill out all required fields.</p>"
}
Relationship Elements
{
type: "relationship",
relationshipId: 0,
label: "Related Inspections",
description: "View and manage related inspection records",
displayCount: 5,
orderByFields: [{
field: "inspection_date",
order: "desc"
}],
editableExpression: "true"
}
Input Types
TextBox Input
{
type: "field",
fieldName: "name",
label: "Name",
input: {
type: "text-box",
maxLength: 100,
minLength: 1
}
}
TextArea Input
{
type: "field",
fieldName: "description",
label: "Description",
input: {
type: "text-area",
maxLength: 1000,
minLength: 0
}
}
ComboBox Input
{
type: "field",
fieldName: "category",
label: "Category",
input: {
type: "combo-box",
showNoValueOption: true,
noValueOptionLabel: "Select a category..."
}
}
// Works with coded value domains - values auto-populate from domain
Radio Buttons Input
{
type: "field",
fieldName: "priority",
label: "Priority",
input: {
type: "radio-buttons",
showNoValueOption: false
}
}
Switch Input
{
type: "field",
fieldName: "is_active",
label: "Active",
input: {
type: "switch",
offValue: 0,
onValue: 1
}
}
DatePicker Input
{
type: "field",
fieldName: "start_date",
label: "Start Date",
input: {
type: "date-picker",
min: new Date("2020-01-01"),
max: new Date("2030-12-31"),
includeTime: false
}
}
DateTimePicker Input
{
type: "field",
fieldName: "event_datetime",
label: "Event Date/Time",
input: {
type: "datetime-picker",
min: new Date("2020-01-01T00:00:00"),
max: new Date("2030-12-31T23:59:59"),
includeTime: true
}
}
TimePicker Input
{
type: "field",
fieldName: "event_time",
label: "Event Time",
input: {
type: "time-picker"
}
}
Barcode Scanner Input
{
type: "field",
fieldName: "barcode",
label: "Barcode",
input: {
type: "barcode-scanner"
}
}
Expression-Based Configuration
Visibility Expressions
const formTemplate = new FormTemplate({
expressionInfos: [
{
name: "show-commercial-fields",
expression: "$feature.property_type == 'commercial'",
},
{
name: "show-residential-fields",
expression: "$feature.property_type == 'residential'",
},
],
elements: [
{ type: "field", fieldName: "property_type", label: "Property Type" },
{
type: "group",
label: "Commercial Details",
visibilityExpression: "show-commercial-fields",
elements: [
{ type: "field", fieldName: "business_name", label: "Business Name" },
{ type: "field", fieldName: "num_employees", label: "Employees" },
],
},
{
type: "group",
label: "Residential Details",
visibilityExpression: "show-residential-fields",
elements: [
{ type: "field", fieldName: "num_bedrooms", label: "Bedrooms" },
{ type: "field", fieldName: "num_bathrooms", label: "Bathrooms" },
],
},
],
});
Required and Editable Expressions
// Conditionally required
{
type: "field",
fieldName: "inspection_notes",
label: "Inspection Notes",
requiredExpression: "notes-required"
}
// Conditionally editable
{
type: "field",
fieldName: "approved_by",
label: "Approved By",
editableExpression: "is-pending"
}
FeatureForm Widget
import FeatureForm from "@arcgis/core/widgets/FeatureForm.js";
const featureForm = new FeatureForm({
container: "formDiv",
layer: featureLayer,
formTemplate: formTemplate,
});
// Set feature to edit
featureForm.feature = selectedGraphic;
// Listen for submit
featureForm.on("submit", () => {
if (featureForm.feature) {
const updated = featureForm.getValues();
featureLayer.applyEdits({
updateFeatures: [
{
attributes: {
...featureForm.feature.attributes,
...updated,
},
geometry: featureForm.feature.geometry,
},
],
});
}
});
// Handle value changes
featureForm.on("value-change", (event) => {
console.log(`${event.fieldName} changed to ${event.value}`);
});
Complete Example: FeatureTable with Map
<!DOCTYPE html>
<html>
<head>
<script src="https://js.arcgis.com/5.0/"></script>
<script
type="module"
src="https://js.arcgis.com/5.0/map-components/"
></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
#container {
display: flex;
flex-direction: column;
height: 100%;
}
arcgis-map {
flex: 1;
}
#tableDiv {
height: 300px;
}
</style>
</head>
<body>
<div id="container">
<arcgis-map
basemap="streets-navigation-vector"
center="-118.805,34.027"
zoom="11"
>
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
<arcgis-feature-table
reference-element="arcgis-map"
editing-enabled
></arcgis-feature-table>
</div>
<script type="module">
const FeatureLayer = await $arcgis.import(
"@arcgis/core/layers/FeatureLayer.js",
);
const mapElement = document.querySelector("arcgis-map");
const view = await mapElement.view;
await view.when();
const featureLayer = new FeatureLayer({
url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0",
});
mapElement.map.add(featureLayer);
const table = document.querySelector("arcgis-feature-table");
table.layer = featureLayer;
// Highlight table selection on map
let highlightHandle;
table.addEventListener("arcgisSelectionChange", async () => {
const layerView = await view.whenLayerView(featureLayer);
if (highlightHandle) {
highlightHandle.remove();
}
const objectIds = table.highlightIds?.toArray();
if (objectIds && objectIds.length > 0) {
highlightHandle = layerView.highlight(objectIds);
}
});
</script>
</body>
</html>
Complete Example: FormTemplate with Editor
<!DOCTYPE html>
<html>
<head>
<script src="https://js.arcgis.com/5.0/"></script>
<script
type="module"
src="https://js.arcgis.com/5.0/map-components/"
></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
</head>
<body>
<arcgis-map
basemap="streets-navigation-vector"
center="-118.805,34.027"
zoom="13"
>
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-editor slot="top-right"></arcgis-editor>
</arcgis-map>
<script type="module">
const FeatureLayer = await $arcgis.import(
"@arcgis/core/layers/FeatureLayer.js",
);
const mapElement = document.querySelector("arcgis-map");
const view = await mapElement.view;
await view.when();
const layer = new FeatureLayer({
url: "https://services.arcgis.com/.../FeatureServer/0",
});
mapElement.map.add(layer);
const editor = document.querySelector("arcgis-editor");
editor.layerInfos = [
{
layer: layer,
formTemplate: {
title: "Property Information",
description: "Enter property details",
preserveFieldValuesWhenHidden: true,
expressionInfos: [
{
name: "is-commercial",
expression: "$feature.type == 'commercial'",
},
],
elements: [
{
type: "field",
fieldName: "name",
label: "Property Name",
input: { type: "text-box", maxLength: 100 },
},
{
type: "field",
fieldName: "type",
label: "Property Type",
input: { type: "combo-box" },
},
{
type: "group",
label: "Commercial Details",
visibilityExpression: "is-commercial",
elements: [
{
type: "field",
fieldName: "business_type",
label: "Business Type",
},
{ type: "field", fieldName: "sqft", label: "Square Footage" },
],
},
{
type: "field",
fieldName: "inspection_date",
label: "Last Inspection",
input: { type: "date-picker" },
},
{
type: "field",
fieldName: "notes",
label: "Notes",
input: { type: "text-area", maxLength: 500 },
},
],
},
},
];
</script>
</body>
</html>
Reference Samples
feature-table- FeatureTable component usagewidgets-featuretable-editing- Editing with FeatureTablewidgets-featuretable-map- FeatureTable with map integrationwidgets-featuretable-relates- FeatureTable with related recordsediting-groupedfeatureform- Grouped feature form layoutediting-featureform-fieldvisibility- Controlling field visibility in forms
Common Pitfalls
-
Field names must match:
fieldNamemust exactly match the layer field name (case-sensitive).// Layer has field "PropertyName" { fieldName: "PropertyName"; } // Correct { fieldName: "propertyname"; } // Wrong - case sensitive -
Coded value domains: ComboBox automatically populates from domain values.
// If field has coded value domain, values come from domain { type: "field", fieldName: "status", input: { type: "combo-box" } } // Dropdown shows domain values automatically - no manual options needed -
Expression names: Reference expressions by name string, not expression text.
expressionInfos: [{ name: "my-expr", expression: "..." }], elements: [{ visibilityExpression: "my-expr" // String reference to name }] -
Layer must be editable: Check capabilities before enabling editing.
await layer.load(); if (layer.capabilities?.editing?.supportsUpdateByOthers) { featureTable.editingEnabled = true; } -
Container size: FeatureTable widget needs explicit height on its container.
#tableDiv { height: 400px; /* Required - table won't render without explicit height */ width: 100%; } -
Highlight ID type:
highlightIdsexpects ObjectID numbers, not strings.featureTable.highlightIds.add(123); // Correct - number featureTable.highlightIds.add("123"); // Wrong - string
Related Skills
- See
arcgis-editingfor Editor configuration, applyEdits, and versioning. - See
arcgis-popup-templatesfor PopupTemplate configuration. - See
arcgis-interactionfor hit testing and map selection. - See
arcgis-layersfor FeatureLayer configuration.
More from saschabrunnerch/arcgis-maps-sdk-js-ai-context
arcgis-core-maps
Create 2D and 3D maps using ArcGIS Maps SDK for JavaScript. Use for initializing maps, scenes, views, and navigation. Supports both Map Components (web components) and Core API approaches.
60arcgis-widgets-ui
Build map user interfaces with ArcGIS widgets, Map Components, and Calcite Design System. Use for adding legends, layer lists, search, tables, time sliders, and custom UI layouts.
55arcgis-geometry-operations
Create, manipulate, and analyze geometries using geometry classes and geometry operators. Use for spatial calculations, geometry creation, buffering, intersections, unions, and mesh operations.
47arcgis-popup-templates
Configure rich popup content with text, fields, media, charts, attachments, and related records. Use when customizing feature popups, adding charts or images to popups, templating popup titles and field formatting, or displaying related record data on click.
45arcgis-imagery
Work with raster and imagery data using ImageryLayer, ImageryTileLayer, pixel filtering, raster functions, multidimensional data, and oriented imagery. Use for satellite imagery, elevation data, and scientific raster datasets.
42arcgis-authentication
Implement authentication with ArcGIS using OAuth 2.0, API keys, and identity management. Use for accessing secured services, portal items, and user-specific content.
40