--- name: arcgis-tables-forms description: Configure FeatureTable for tabular data display and FormTemplate with input elements for feature editing forms. Use when adding spreadsheet-style data grids, building attribute edit forms, configuring form input types and validation, or surfacing related records in forms. --- # ArcGIS Tables & Forms Use this skill for configuring FeatureTable components/widgets and FormTemplate with various input elements. ## Import Patterns ### Direct ESM Imports ```javascript 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) ```javascript 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 ```html ``` ### 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 ```html ``` ## FeatureTable Widget (Core API) ### Basic Widget ```javascript import FeatureTable from "@arcgis/core/widgets/FeatureTable.js"; const featureTable = new FeatureTable({ view: view, layer: featureLayer, container: "tableDiv", }); ``` ### Configured Widget ```javascript 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 ```javascript 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 ```javascript 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. ```javascript 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 ```javascript // 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 ```javascript 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 ```javascript // 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 ```javascript // 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 ```javascript 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 ```javascript { 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 ```javascript { 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 ```javascript { type: "text", text: "

Important Instructions

Please fill out all required fields.

" } ``` ### Relationship Elements ```javascript { 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 ```javascript { type: "field", fieldName: "name", label: "Name", input: { type: "text-box", maxLength: 100, minLength: 1 } } ``` ### TextArea Input ```javascript { type: "field", fieldName: "description", label: "Description", input: { type: "text-area", maxLength: 1000, minLength: 0 } } ``` ### ComboBox Input ```javascript { 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 ```javascript { type: "field", fieldName: "priority", label: "Priority", input: { type: "radio-buttons", showNoValueOption: false } } ``` ### Switch Input ```javascript { type: "field", fieldName: "is_active", label: "Active", input: { type: "switch", offValue: 0, onValue: 1 } } ``` ### DatePicker Input ```javascript { 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 ```javascript { 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 ```javascript { type: "field", fieldName: "event_time", label: "Event Time", input: { type: "time-picker" } } ``` ### Barcode Scanner Input ```javascript { type: "field", fieldName: "barcode", label: "Barcode", input: { type: "barcode-scanner" } } ``` ## Expression-Based Configuration ### Visibility Expressions ```javascript 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 ```javascript // 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 ```javascript 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 ```html
``` ## Complete Example: FormTemplate with Editor ```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). ```javascript // Layer has field "PropertyName" { fieldName: "PropertyName"; } // Correct { fieldName: "propertyname"; } // Wrong - case sensitive ``` 2. **Coded value domains**: ComboBox automatically populates from domain values. ```javascript // 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. ```javascript expressionInfos: [{ name: "my-expr", expression: "..." }], elements: [{ visibilityExpression: "my-expr" // String reference to name }] ``` 4. **Layer must be editable**: Check capabilities before enabling editing. ```javascript await layer.load(); if (layer.capabilities?.editing?.supportsUpdateByOthers) { featureTable.editingEnabled = true; } ``` 5. **Container size**: FeatureTable widget needs explicit height on its container. ```css #tableDiv { height: 400px; /* Required - table won't render without explicit height */ width: 100%; } ``` 6. **Highlight ID type**: `highlightIds` expects ObjectID numbers, not strings. ```javascript 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.