mcpField
A
arcgis-tables-forms

arcgis-tables-forms

github.com/saschabrunnerchDevelopment2363 installsUpdated 29w ago
Repository

About arcgis-tables-forms

A reference skill for ArcGIS Maps SDK for JavaScript developers building feature tables and attribute-editing forms. It explains table configuration, selection, column layouts, form inputs, and expression-based behavior.

Key features

  1. 01Configure FeatureTable columns and display
  2. 02Edit records inline and manage selections
  3. 03Build forms with varied input types
  4. 04Group fields and show related records
  5. 05Control visibility and validation with expressions

Use cases

  1. 01Create an editable attribute table for a layer
  2. 02Design structured feature-editing forms
  3. 03Synchronize table selection with map features

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/saschabrunnerch/arcgis-maps-sdk-js-ai-context --skill arcgis-tables-forms

After installing, type/arcgis-tables-formsin Claude Code to confirm it loaded.

Details

From the project README

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" with const 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
PropertyTypeDefaultDescription
layerFeatureLayerThe layer to display
attribute-table-templateAttributeTableTemplateTable configuration (new in 5.0)
editing-enabledbooleanfalseEnable inline editing
filter-by-selection-enabledbooleanfalseFilter by selected features
highlight-disabledbooleanfalseDisable row highlighting
hide-headerbooleanfalseHide the table header
hide-menubooleanfalseHide the table menu
hide-selection-columnbooleanfalseHide the selection column
multiple-selection-disabledbooleanfalseDisable multi-row selection
multiple-sort-enabledbooleanfalseEnable multi-column sorting
page-sizenumberRows per page
auto-save-disabledbooleanfalseDisable auto-saving edits
definition-expressionstringFilter expression
time-zonestringTime zone for dates
Hide Menu Items
PropertyDescription
hide-menu-items-clear-selectionHide clear selection menu item
hide-menu-items-delete-selectionHide delete selection menu item
hide-menu-items-export-selection-to-csvHide export to CSV menu item
hide-menu-items-refresh-dataHide refresh data menu item
hide-menu-items-toggle-columnsHide toggle columns menu item
hide-menu-items-zoom-to-selectionHide zoom to selection menu item
Key Events
EventDescription
arcgisSelectionChangeFires when row selection changes
arcgisCellClickFires when a cell is clicked
arcgisCellKeydownFires on keyboard interaction in a cell
arcgisCellPointeroverFires when pointer enters a cell
arcgisCellPointeroutFires when pointer leaves a cell
arcgisColumnReorderFires when columns are reordered
arcgisPropertyChangeFires when a property changes
arcgisReadyFires 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
PropertyTypeDescription
fieldNamestringField to display
labelstringColumn header text
editablebooleanAllow editing
visiblebooleanColumn visibility
frozenbooleanFreeze column to left
frozenToEndbooleanFreeze column to right
resizablebooleanAllow column resizing
autoWidthbooleanAuto-size column width
widthnumberColumn width in pixels
textAlignstringText alignment
textWrapbooleanWrap text in cells
initialSortPrioritynumberSort 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
TypeClassDescription
fieldAttributeTableFieldElementIndividual field column
groupAttributeTableGroupElementGroup of columns
relationshipAttributeTableRelationshipElementRelated records
attachmentAttributeTableAttachmentElementAttachment 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
PropertyTypeDescription
titlestringForm title
descriptionstringForm description
elementsFormElement[]Array of form elements
expressionInfosExpressionInfo[]Arcade expression definitions
preserveFieldValuesWhenHiddenbooleanKeep hidden field values
Form Element Types
TypeDescription
fieldFieldElement - Edit a specific field
groupGroupElement - Group of elements
textTextElement - Static text/HTML content
relationshipRelationshipElement - Related records
attachmentAttachmentElement - 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 usage
  • widgets-featuretable-editing - Editing with FeatureTable
  • widgets-featuretable-map - FeatureTable with map integration
  • widgets-featuretable-relates - FeatureTable with related records
  • editing-groupedfeatureform - Grouped feature form layout
  • editing-featureform-fieldvisibility - Controlling field visibility in forms

Common Pitfalls

  1. Field names must match: fieldName must exactly match the layer field name (case-sensitive).

    // Layer has field "PropertyName"
    {
      fieldName: "PropertyName";
    } // Correct
    {
      fieldName: "propertyname";
    } // Wrong - case sensitive
    
  2. 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
    
  3. Expression names: Reference expressions by name string, not expression text.

    expressionInfos: [{ name: "my-expr", expression: "..." }],
    elements: [{
      visibilityExpression: "my-expr"  // String reference to name
    }]
    
  4. Layer must be editable: Check capabilities before enabling editing.

    await layer.load();
    if (layer.capabilities?.editing?.supportsUpdateByOthers) {
      featureTable.editingEnabled = true;
    }
    
  5. Container size: FeatureTable widget needs explicit height on its container.

    #tableDiv {
      height: 400px; /* Required - table won't render without explicit height */
      width: 100%;
    }
    
  6. Highlight ID type: highlightIds expects ObjectID numbers, not strings.

    featureTable.highlightIds.add(123); // Correct - number
    featureTable.highlightIds.add("123"); // Wrong - string
    

Related Skills

  • See arcgis-editing for Editor configuration, applyEdits, and versioning.
  • See arcgis-popup-templates for PopupTemplate configuration.
  • See arcgis-interaction for hit testing and map selection.
  • See arcgis-layers for FeatureLayer configuration.

Comments

Markdown supported