askill
arcgis-tables-forms

arcgis-tables-formsSafety 95Repository

Configure FeatureTable widget and FormTemplate with input elements. Use for displaying attribute data in tables, customizing edit forms, and configuring field inputs.

5 stars
1.2k downloads
Updated 2/24/2026

Package Files

Loading files...
SKILL.md

ArcGIS Tables & Forms

Use this skill for configuring FeatureTable widgets and FormTemplate with various input elements.

FeatureTable

Display feature attributes in an interactive table.

FeatureTable Component

<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");

  await map.viewOnReady();

  // Set the layer for the table
  const layer = map.view.map.layers.find(l => l.type === "feature");
  table.layer = layer;
</script>

FeatureTable Widget (Core API)

import FeatureTable from "@arcgis/core/widgets/FeatureTable.js";

const featureTable = new FeatureTable({
  view: view,
  layer: featureLayer,
  container: "tableDiv"
});

With Field Configuration

const featureTable = new FeatureTable({
  view: view,
  layer: featureLayer,
  container: "tableDiv",
  fieldConfigs: [
    {
      name: "name",
      label: "Name",
      direction: "asc"  // Initial sort
    },
    {
      name: "category",
      label: "Category"
    },
    {
      name: "value",
      label: "Value",
      format: {
        digitSeparator: true,
        places: 2
      }
    },
    {
      name: "date_created",
      label: "Created",
      format: {
        dateFormat: "short-date"
      }
    }
  ]
});

FeatureTable Configuration

const featureTable = new FeatureTable({
  view: view,
  layer: featureLayer,
  container: "tableDiv",

  // Display options
  visibleElements: {
    header: true,
    menu: true,
    menuItems: {
      clearSelection: true,
      refreshData: true,
      toggleColumns: true,
      selectedRecordsShowAllToggle: true,
      selectedRecordsShowSelectedToggle: true,
      zoomToSelection: true
    },
    selectionColumn: true,
    columnMenus: true
  },

  // Table behavior
  multiSortEnabled: true,
  editingEnabled: true,
  highlightEnabled: true,
  attachmentsEnabled: true,
  relatedRecordsEnabled: true,

  // Pagination
  pageSize: 50,

  // Initial state
  filterGeometry: view.extent,  // Only show features in view
  highlightOnRowSelectEnabled: true
});

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",
        sortable: true,
        initialSortPriority: 0,
        direction: "asc"
      }),
      new FieldColumnTemplate({
        fieldName: "status",
        label: "Status",
        menuConfig: {
          items: [{
            label: "Custom Action",
            iconClass: "esri-icon-settings",
            clickFunction: (event) => {
              console.log("Custom action on:", event.feature);
            }
          }]
        }
      }),
      new FieldColumnTemplate({
        fieldName: "value",
        label: "Value ($)",
        textAlign: "right",
        formatFunction: (info) => {
          return `$${info.value.toLocaleString()}`;
        }
      })
    ]
  }
});

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" })
        ]
      })
    ]
  }
});

FeatureTable Events

// Row selection
featureTable.on("selection-change", (event) => {
  console.log("Added:", event.added);
  console.log("Removed:", event.removed);

  // Get all selected features
  const selectedFeatures = featureTable.highlightIds.toArray();
});

// Row click (double-click to zoom)
featureTable.viewModel.on("row-highlight-change", (event) => {
  if (event.feature) {
    view.goTo(event.feature.geometry);
  }
});

// Editing complete
featureTable.on("edit-complete", (event) => {
  console.log("Edited feature:", event.feature);
  console.log("Updated attributes:", event.attributes);
});

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);

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;

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.layer === featureLayer);

  if (feature) {
    featureTable.highlightIds.removeAll();
    featureTable.highlightIds.add(feature.graphic.attributes.OBJECTID);
  }
});

// Table selection highlights on map
featureTable.on("selection-change", async (event) => {
  const layerView = await view.whenLayerView(featureLayer);

  if (highlightHandle) {
    highlightHandle.remove();
  }

  const objectIds = featureTable.highlightIds.toArray();
  if (objectIds.length > 0) {
    const query = featureLayer.createQuery();
    query.objectIds = objectIds;
    const results = await featureLayer.queryFeatures(query);
    highlightHandle = layerView.highlight(results.features);
  }
});

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;

Field Elements

import FieldElement from "@arcgis/core/form/elements/FieldElement.js";

const fieldElement = new FieldElement({
  fieldName: "name",
  label: "Name",
  description: "Enter the feature name",
  hint: "Required field",
  requiredExpression: "true",
  editableExpression: "$feature.status != 'locked'",
  visibilityExpression: "$feature.type != 'hidden'"
});

Group Elements

import GroupElement from "@arcgis/core/form/elements/GroupElement.js";

const formTemplate = new FormTemplate({
  elements: [
    new GroupElement({
      label: "Basic Information",
      description: "Enter basic details",
      elements: [
        { type: "field", fieldName: "name", label: "Name" },
        { type: "field", fieldName: "type", label: "Type" }
      ]
    }),
    new GroupElement({
      label: "Location",
      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

import TextElement from "@arcgis/core/form/elements/TextElement.js";

const formTemplate = new FormTemplate({
  elements: [
    new TextElement({
      type: "text",
      text: "<h3>Important Instructions</h3><p>Please fill out all required fields.</p>"
    }),
    { type: "field", fieldName: "name", label: "Name" },
    new TextElement({
      text: "<hr><small>Fields below are optional</small>"
    }),
    { type: "field", fieldName: "notes", label: "Notes" }
  ]
});

Relationship Elements

import RelationshipElement from "@arcgis/core/form/elements/RelationshipElement.js";

const formTemplate = new FormTemplate({
  elements: [
    { type: "field", fieldName: "name", label: "Name" },
    new RelationshipElement({
      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
// Domain values automatically populate the combo box

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 Expressions

{
  type: "field",
  fieldName: "inspection_notes",
  label: "Inspection Notes",
  requiredExpression: "$feature.inspection_result == 'failed'"
}

Editable Expressions

{
  type: "field",
  fieldName: "approved_by",
  label: "Approved By",
  editableExpression: "$feature.status == 'pending'"
}

FeatureForm Widget

Widget for editing feature attributes.

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}`);
});

AttributeTableTemplate

Configure attribute table in Editor widget.

import AttributeTableTemplate from "@arcgis/core/widgets/Editor/support/AttributeTableTemplate.js";
import AttributeTableFieldElement from "@arcgis/core/widgets/Editor/support/AttributeTableFieldElement.js";

const tableTemplate = new AttributeTableTemplate({
  elements: [
    new AttributeTableFieldElement({
      fieldName: "name",
      label: "Name",
      editable: true
    }),
    new AttributeTableFieldElement({
      fieldName: "status",
      label: "Status",
      editable: true
    })
  ]
});

Editor Widget Integration

import Editor from "@arcgis/core/widgets/Editor.js";

const editor = new Editor({
  view: view,
  layerInfos: [{
    layer: featureLayer,
    formTemplate: formTemplate,
    enabled: true,
    addEnabled: true,
    updateEnabled: true,
    deleteEnabled: true
  }]
});

view.ui.add(editor, "top-right");

Common Patterns

Complete Form Setup

const formTemplate = new FormTemplate({
  title: "Property Information",
  description: "Enter property details",
  preserveFieldValuesWhenHidden: true,
  expressionInfos: [
    {
      name: "is-commercial",
      expression: "$feature.type == 'commercial'"
    }
  ],
  elements: [
    // Header text
    {
      type: "text",
      text: "<b>Basic Information</b>"
    },
    // Required field
    {
      type: "field",
      fieldName: "name",
      label: "Property Name",
      requiredExpression: "true",
      input: { type: "text-box", maxLength: 100 }
    },
    // Dropdown
    {
      type: "field",
      fieldName: "type",
      label: "Property Type",
      input: { type: "combo-box" }
    },
    // Conditional group
    {
      type: "group",
      label: "Commercial Details",
      visibilityExpression: "is-commercial",
      elements: [
        { type: "field", fieldName: "business_type", label: "Business Type" },
        { type: "field", fieldName: "sqft", label: "Square Footage" }
      ]
    },
    // Date field
    {
      type: "field",
      fieldName: "inspection_date",
      label: "Last Inspection",
      input: { type: "date-picker" }
    },
    // Long text
    {
      type: "field",
      fieldName: "notes",
      label: "Notes",
      input: { type: "text-area", maxLength: 500 }
    }
  ]
});

FeatureTable with Editing

const featureTable = new FeatureTable({
  view: view,
  layer: featureLayer,
  container: "tableDiv",
  editingEnabled: true,
  fieldConfigs: [
    { name: "name", label: "Name", editable: true },
    { name: "status", label: "Status", editable: true },
    { name: "created_date", label: "Created", editable: false }
  ]
});

featureTable.on("edit-complete", async (event) => {
  console.log("Edit saved:", event.feature.attributes);

  // Refresh related data
  await featureLayer.refresh();
});

Responsive Table Layout

const featureTable = new FeatureTable({
  view: view,
  layer: featureLayer,
  container: "tableDiv",
  autoRefreshEnabled: true,
  pageSize: 25
});

// Resize handling
window.addEventListener("resize", () => {
  featureTable.refresh();
});

// Toggle visibility
function toggleTable(visible) {
  document.getElementById("tableDiv").style.display = visible ? "block" : "none";
  if (visible) {
    featureTable.refresh();
  }
}

TypeScript Usage

Form elements use autocasting with type properties. For TypeScript safety, use as const:

// Use 'as const' for type safety in form templates
const formTemplate = {
  title: "Edit Feature",
  elements: [
    {
      type: "field",
      fieldName: "name",
      label: "Name"
    },
    {
      type: "group",
      label: "Address",
      elements: [
        { type: "field", fieldName: "street" },
        { type: "field", fieldName: "city" }
      ]
    }
  ]
} as const;

// For input types
const formElement = {
  type: "field",
  fieldName: "status",
  input: { type: "combo-box" }
} as const;

Tip: See arcgis-core-maps skill for detailed guidance on autocasting vs explicit classes.

Common Pitfalls

  1. Field Names Must Match: fieldName must exactly match layer field

    // Layer has field "PropertyName"
    { fieldName: "PropertyName" }  // Correct
    { fieldName: "propertyname" }  // Wrong - case sensitive
    
  2. Coded Value Domains: ComboBox auto-populates from domain

    // If field has coded value domain, values come from domain
    { type: "field", fieldName: "status", input: { type: "combo-box" } }
    // Dropdown shows domain values automatically
    
  3. Expression Names: Reference expressions by name string

    expressionInfos: [{ name: "my-expr", expression: "..." }],
    elements: [{
      visibilityExpression: "my-expr"  // String reference
    }]
    
  4. Layer Must Be Editable: For edit features to work

    // Layer capabilities must include editing
    if (layer.capabilities.editing.supportsUpdateByOthers) {
      featureTable.editingEnabled = true;
    }
    
  5. Container Size: Table needs explicit height

    #tableDiv {
      height: 400px;  /* Required */
      width: 100%;
    }
    

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

84/100Analyzed 2/23/2026

Comprehensive technical reference for ArcGIS FeatureTable and FormTemplate configuration. Well-structured with extensive code examples covering all major features including field configs, column templates, events, filtering, and various input types. The content appears truncated at the end but covers substantial ground. Tags and clear usage context provided. High-density technical content that would be highly actionable for ArcGIS developers.

95
85
85
75
90

Metadata

Licenseunknown
Version-
Updated2/24/2026
PublisherSaschaBrunnerCH

Tags

api