ArcGIS Editing
Use this skill for editing features including subtypes, forms, versioning, and editor configuration.
Note: For basic editing, use the arcgis-editor component. This skill covers advanced configurations that require the Core API.
Editor Component (Basic)
<arcgis-map item-id="YOUR_EDITABLE_WEBMAP_ID"> <arcgis-editor slot="top-right"></arcgis-editor> </arcgis-map>
Editor Configuration (Advanced)
Configurable Editor
import Editor from "@arcgis/core/widgets/Editor.js";
const editor = new Editor({ view: view, layerInfos: [{ layer: featureLayer, formTemplate: { title: "Feature Details", description: "Enter feature information", elements: [ { type: "field", fieldName: "name", label: "Name", description: "Enter the feature name" }, { type: "field", fieldName: "category", label: "Category" } ] }, // Enable/disable operations addEnabled: true, updateEnabled: true, deleteEnabled: false }] });
view.ui.add(editor, "top-right");
Feature Form
Standalone Feature Form
import FeatureForm from "@arcgis/core/widgets/FeatureForm.js";
const form = new FeatureForm({ container: "formDiv", layer: featureLayer, formTemplate: { title: "Edit Feature", elements: [ { type: "field", fieldName: "name", label: "Name" }, { type: "group", label: "Location Details", elements: [ { type: "field", fieldName: "address" }, { type: "field", fieldName: "city" }, { type: "field", fieldName: "zip" } ] } ] } });
// Set feature to edit form.feature = graphic;
// Listen for submit form.on("submit", async () => { if (form.valid) { const values = form.getValues(); graphic.attributes = { ...graphic.attributes, ...values }; await featureLayer.applyEdits({ updateFeatures: [graphic] }); } });
Form with Grouped Elements
const formTemplate = { title: "Asset Details", elements: [ { type: "group", label: "Basic Information", elements: [ { type: "field", fieldName: "asset_id" }, { type: "field", fieldName: "asset_name" }, { type: "field", fieldName: "asset_type" } ] }, { type: "group", label: "Maintenance", elements: [ { type: "field", fieldName: "last_inspection" }, { type: "field", fieldName: "next_inspection" }, { type: "field", fieldName: "condition" } ] } ] };
Expression-Based Visibility
const formTemplate = { elements: [ { type: "field", fieldName: "type" }, { type: "field", fieldName: "subtype", visibilityExpression: "type-requires-subtype" } ], expressionInfos: [{ name: "type-requires-subtype", expression: "$feature.type == 'complex'" }] };
Subtypes
Working with Subtypes
// Layer with subtypes const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
await layer.load();
// Get subtype info const subtypeField = layer.subtypeField; const subtypes = layer.subtypes;
subtypes.forEach(subtype => { console.log("Code:", subtype.code); console.log("Name:", subtype.name); console.log("Default values:", subtype.defaultValues); });
Subtype Group Layer
import SubtypeGroupLayer from "@arcgis/core/layers/SubtypeGroupLayer.js";
const subtypeLayer = new SubtypeGroupLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
await subtypeLayer.load();
// Access sublayers by subtype subtypeLayer.sublayers.forEach(sublayer => { console.log("Subtype:", sublayer.subtypeCode, sublayer.title); // Each sublayer can have different renderer/popup });
Editor with Subtype Support
const editor = new Editor({ view: view, layerInfos: [{ layer: subtypeGroupLayer, // Editor automatically handles subtypes addEnabled: true, updateEnabled: true }] });
Versioning
Branch Versioning Overview
Branch versioning allows multiple users to edit the same data simultaneously without conflicts until reconciliation.
import VersionManagementService from "@arcgis/core/versionManagement/VersionManagementService.js";
const vms = new VersionManagementService({ url: "https://services.arcgis.com/.../VersionManagementServer" });
await vms.load();
console.log("Default version:", vms.defaultVersionIdentifier);
Get Version Information
// Get all versions const versions = await vms.getVersionInfos(); versions.forEach(v => { console.log("Version:", v.versionName); console.log(" Owner:", v.versionOwner); console.log(" Description:", v.description); console.log(" Access:", v.access); // public, protected, private console.log(" Parent:", v.parentVersionName); console.log(" Created:", v.creationDate); console.log(" Modified:", v.modifiedDate); });
// Get specific version info const versionInfo = await vms.getVersionInfo({ versionName: "sde.MyVersion" });
Create Version
// Create new version const newVersion = await vms.createVersion({ versionName: "MyEditVersion", description: "Version for editing project X", access: "private", // public, protected, private parentVersionName: "sde.DEFAULT" // Optional, defaults to DEFAULT });
console.log("Created version:", newVersion.versionName); console.log("Version GUID:", newVersion.versionGuid);
Delete Version
// Delete a version (must be owner or admin) await vms.deleteVersion({ versionName: "sde.MyEditVersion" });
Alter Version
// Modify version properties await vms.alterVersion({ versionName: "sde.MyEditVersion", description: "Updated description", access: "protected", // Change access level ownerName: "newOwner" // Transfer ownership (admin only) });
Switching Versions
// Set version on layer at creation const featureLayer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0", gdbVersion: "sde.MyEditVersion" });
// Or change version dynamically await featureLayer.load(); featureLayer.gdbVersion = "sde.AnotherVersion"; await featureLayer.refresh();
// Switch all layers in map map.layers.forEach(layer => { if (layer.gdbVersion !== undefined) { layer.gdbVersion = "sde.MyEditVersion"; layer.refresh(); } });
Start/Stop Edit Session
// Get the version identifier const versionInfos = await vms.getVersionInfos(); const versionIdentifier = versionInfos.find( v => v.versionName === "sde.MyEditVersion" ).versionIdentifier;
// Start reading session await vms.startReading({ versionIdentifier: versionIdentifier });
// Start editing session (for write access) await vms.startEditing({ versionIdentifier: versionIdentifier });
// Stop editing when done await vms.stopEditing({ versionIdentifier: versionIdentifier, saveEdits: true // or false to discard });
// Stop reading when done await vms.stopReading({ versionIdentifier: versionIdentifier });
Version Reconcile and Post
// Reconcile version with parent const reconcileResult = await vms.reconcile({ versionIdentifier: versionIdentifier, abortIfConflicts: false, conflictDetection: "by-attribute", // by-attribute, by-object withPost: false, conflictResolution: "favorEditVersion" // favorEditVersion, favorTargetVersion });
console.log("Has conflicts:", reconcileResult.hasConflicts);
if (!reconcileResult.hasConflicts) { // Post changes to parent version await vms.post({ versionIdentifier }); console.log("Changes posted successfully"); }
Conflict Detection and Resolution
// Check for conflicts before reconcile const conflictResult = await vms.reconcile({ versionIdentifier: versionIdentifier, abortIfConflicts: true, // Stop if conflicts found conflictDetection: "by-attribute" });
if (conflictResult.hasConflicts) { // Get conflict details console.log("Conflicts found, manual resolution needed");
// Resolve conflicts by favoring edit version const resolveResult = await vms.reconcile({ versionIdentifier: versionIdentifier, abortIfConflicts: false, conflictResolution: "favorEditVersion", withPost: true }); }
Complete Version Workflow
async function editInVersion(vms, featureLayer, edits) {
// 1. Create version
const version = await vms.createVersion({
versionName: Edit_${Date.now()},
description: "Temporary edit version",
access: "private"
});
const versionIdentifier = version.versionIdentifier;
try { // 2. Switch layer to version featureLayer.gdbVersion = version.versionName; await featureLayer.refresh();
// 3. Start edit session
await vms.startEditing({
versionIdentifier: versionIdentifier
});
// 4. Apply edits
await featureLayer.applyEdits(edits);
// 5. Reconcile with parent
const reconcileResult = await vms.reconcile({
versionIdentifier: versionIdentifier,
abortIfConflicts: false,
conflictResolution: "favorEditVersion",
withPost: false
});
if (reconcileResult.hasConflicts) {
throw new Error("Conflicts detected during reconcile");
}
// 6. Post to parent
await vms.post({ versionIdentifier });
// 7. Stop editing
await vms.stopEditing({
versionIdentifier: versionIdentifier,
saveEdits: true
});
console.log("Edits posted successfully");
} finally { // 8. Switch back to default and delete temp version featureLayer.gdbVersion = vms.defaultVersionIdentifier; await featureLayer.refresh();
await vms.deleteVersion({
versionName: version.versionName
});
} }
Related Records
Query Related Records
const relatedRecords = await layer.queryRelatedFeatures({ outFields: ["*"], relationshipId: 0, objectIds: [selectedFeature.attributes.OBJECTID] });
relatedRecords[selectedObjectId].features.forEach(related => { console.log("Related:", related.attributes); });
Attachments
Enable Attachments in Editor
const editor = new Editor({ view: view, layerInfos: [{ layer: featureLayer, attachmentsOnCreateEnabled: true, attachmentsOnUpdateEnabled: true }] });
Programmatic Attachment Handling
// Query attachments const attachments = await layer.queryAttachments({ objectIds: [featureOID] });
// Add attachment const formData = new FormData(); formData.append("attachment", fileBlob, "photo.jpg");
await layer.addAttachment(featureOID, formData);
// Delete attachment await layer.deleteAttachments(featureOID, [attachmentId]);
Validation
Form Validation
const form = new FeatureForm({ layer: featureLayer, formTemplate: { elements: [{ type: "field", fieldName: "email", label: "Email", // Use requiredExpression to conditionally require a field requiredExpression: "email-required" }], expressionInfos: [{ name: "email-required", expression: "$feature.contact_method == 'email'" }] } });
// Available expression types on FieldElement: // - visibilityExpression: controls field visibility // - editableExpression: controls whether field is editable // - requiredExpression: controls whether field is required // - valueExpression: computes a calculated value for the field
// Check validity before submit if (form.valid) { // Submit } else { // Show validation errors }
Complete Example
Map Components (Recommended)
<!DOCTYPE html> <html> <head> <script type="module" src="https://js.arcgis.com/calcite-components/3.3.3/calcite.esm.js"></script> <script src="https://js.arcgis.com/4.34/"></script> <script type="module" src="https://js.arcgis.com/4.34/map-components/"></script> <style> html, body { height: 100%; margin: 0; } </style> </head> <body> <arcgis-map basemap="streets-navigation-vector"> <arcgis-zoom slot="top-left"></arcgis-zoom> <arcgis-editor slot="top-right"></arcgis-editor> </arcgis-map>
<script type="module"> import FeatureLayer from "@arcgis/core/layers/FeatureLayer.js";
const mapElement = document.querySelector("arcgis-map");
await mapElement.viewOnReady();
const layer = new FeatureLayer({
url: "https://services.arcgis.com/.../FeatureServer/0"
});
mapElement.map.add(layer);
</script> </body> </html>
Core API
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="https://js.arcgis.com/4.34/esri/themes/light/main.css" /> <script src="https://js.arcgis.com/4.34/"></script> <style> html, body, #viewDiv { height: 100%; margin: 0; } </style> <script type="module"> import Map from "@arcgis/core/Map.js"; import MapView from "@arcgis/core/views/MapView.js"; import FeatureLayer from "@arcgis/core/layers/FeatureLayer.js"; import Editor from "@arcgis/core/widgets/Editor.js";
const layer = new FeatureLayer({
url: "https://services.arcgis.com/.../FeatureServer/0"
});
const map = new Map({
basemap: "streets-navigation-vector",
layers: [layer]
});
const view = new MapView({
container: "viewDiv",
map: map
});
const editor = new Editor({
view: view,
layerInfos: [{
layer: layer,
formTemplate: {
title: "Edit Feature",
elements: [
{
type: "group",
label: "Details",
elements: [
{ type: "field", fieldName: "name", label: "Name" },
{ type: "field", fieldName: "type", label: "Type" },
{ type: "field", fieldName: "status", label: "Status" }
]
}
]
},
attachmentsOnCreateEnabled: true,
attachmentsOnUpdateEnabled: true
}]
});
view.ui.add(editor, "top-right");
</script> </head> <body> <div id="viewDiv"></div> </body> </html>
TypeScript Usage
Form elements use autocasting with type properties. For TypeScript safety, use as const :
// Use 'as const' for type safety in editor configurations const editor = new Editor({ view: view, layerInfos: [{ layer: featureLayer, formTemplate: { title: "Feature Details", elements: [ { type: "field", fieldName: "name" }, { type: "field", fieldName: "category" } ] } as const }] });
Tip: See arcgis-core-maps skill for detailed guidance on autocasting vs explicit classes.
Editing Components
Component Purpose
arcgis-version-management
Manage and switch between geodatabase versions
Reference Samples
-
widgets-editor-configurable
-
Configurable Editor widget
-
widgets-editor-subtypes
-
Editing with subtype group layers
-
editing-featureform-fieldvisibility
-
Controlling field visibility in forms
-
changing-version
-
Switching geodatabase versions
Common Pitfalls
Editing permissions: Calling applyEdits on a layer without checking edit capabilities causes silent failures.
// Anti-pattern: calling applyEdits without checking capabilities const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" }); await layer.applyEdits({ addFeatures: [newFeature] }); // May fail silently or throw a cryptic server error
// Correct: check capabilities before editing const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" }); await layer.load(); if (layer.editingEnabled && layer.capabilities?.editing?.supportsAddingFeatures) { await layer.applyEdits({ addFeatures: [newFeature] }); } else { console.error("Layer does not support adding features"); }
Impact: The applyEdits call fails with a cryptic server error or silently returns an error result that is easy to miss, leaving the user believing the edit was saved when it was not.
Subtype field: Must match the subtype configuration in the service
Version locking: Editing branch-versioned data without proper session management causes version locks.
// Anti-pattern: editing without version management session const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" }); layer.gdbVersion = "editor1.design_v1"; await layer.applyEdits({ updateFeatures: [updatedFeature] }); // Version remains locked, blocking other users
// Correct: start and stop version management session const versionManagement = new VersionManagementService({ url: "https://services.arcgis.com/.../VersionManagementServer" }); const versionName = "editor1.design_v1";
// Start editing session await versionManagement.startEditing(versionName);
const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" }); layer.gdbVersion = versionName; await layer.applyEdits({ updateFeatures: [updatedFeature] });
// Stop editing session to release the lock await versionManagement.stopEditing(versionName, true); // true = save edits
Impact: The version stays locked after editing, preventing other users from accessing or editing it. Accumulated locks require administrator intervention to release.
Validation expressions: Must return true/false or error object
Related records: Require proper relationship configuration in service