arcgis-editing-advanced
ArcGIS Advanced Editing
Use this skill for advanced editing features including subtypes, forms, versioning, and editor configuration.
Note: For basic editing, use the
arcgis-editorcomponent. 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");
Editor with Field Configuration
const editor = new Editor({
view: view,
layerInfos: [{
layer: featureLayer,
fieldConfig: [
{
name: "status",
label: "Status",
editable: true,
required: true
},
{
name: "created_date",
label: "Created",
editable: false // Read-only
},
{
name: "comments",
label: "Comments",
editable: true,
maxLength: 500
}
]
}]
});
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.defaultVersionName);
console.log("Supports versioning:", vms.supportsVersioning);
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
// Start editing session
const session = await vms.startReading({
versionName: "sde.MyEditVersion"
});
const sessionId = session.sessionId;
// For write access
const writeSession = await vms.startEditing({
versionName: "sde.MyEditVersion"
});
// Stop session when done
await vms.stopEditing({
sessionId: sessionId,
saveEdits: true // or false to discard
});
Version Reconcile and Post
// Reconcile version with parent
const reconcileResult = await vms.reconcile({
sessionId: sessionId,
abortIfConflicts: false,
conflictDetection: "byAttribute", // byAttribute, byObject
withPost: false,
conflictResolution: "favorEditVersion" // favorEditVersion, favorTargetVersion
});
console.log("Has conflicts:", reconcileResult.hasConflicts);
console.log("Conflicts removed:", reconcileResult.conflictsRemoved);
if (!reconcileResult.hasConflicts) {
// Post changes to parent version
await vms.post({ sessionId });
console.log("Changes posted successfully");
}
Conflict Detection and Resolution
// Check for conflicts before reconcile
const conflictResult = await vms.reconcile({
sessionId: sessionId,
abortIfConflicts: true, // Stop if conflicts found
conflictDetection: "byAttribute"
});
if (conflictResult.hasConflicts) {
// Get conflict details
console.log("Conflicts found, manual resolution needed");
// Resolve conflicts by favoring edit version
const resolveResult = await vms.reconcile({
sessionId: sessionId,
abortIfConflicts: false,
conflictResolution: "favorEditVersion",
withPost: true
});
}
Version Differences
// Get differences between versions
const differences = await vms.getVersionDifferences({
sessionId: sessionId,
fromMoment: "commonAncestor", // commonAncestor, now
layers: ["0", "1"] // Layer IDs to compare
});
differences.forEach(diff => {
console.log("Layer:", diff.layerId);
console.log("Inserts:", diff.inserts);
console.log("Updates:", diff.updates);
console.log("Deletes:", diff.deletes);
});
Version Lock Management
// Get lock status
const lockInfo = await vms.getLockInfo({
versionName: "sde.MyEditVersion"
});
console.log("Is locked:", lockInfo.isLocked);
console.log("Locked by:", lockInfo.lockOwner);
console.log("Lock type:", lockInfo.lockType);
// Acquire exclusive lock
await vms.acquireLock({
versionName: "sde.MyEditVersion",
lockType: "exclusive" // shared, exclusive
});
// Release lock
await vms.releaseLock({
versionName: "sde.MyEditVersion"
});
Validate Network Topology
// For utility networks - validate topology after edits
const validateResult = await vms.validateNetworkTopology({
sessionId: sessionId,
validateArea: extent, // Optional extent to validate
validationType: "normal" // normal, rebuild
});
console.log("Validation success:", validateResult.success);
console.log("Dirty areas remaining:", validateResult.dirtyAreaCount);
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"
});
try {
// 2. Switch layer to version
featureLayer.gdbVersion = version.versionName;
await featureLayer.refresh();
// 3. Start edit session
const session = await vms.startEditing({
versionName: version.versionName
});
// 4. Apply edits
await featureLayer.applyEdits(edits);
// 5. Reconcile with parent
const reconcileResult = await vms.reconcile({
sessionId: session.sessionId,
abortIfConflicts: false,
conflictResolution: "favorEditVersion",
withPost: false
});
if (reconcileResult.hasConflicts) {
throw new Error("Conflicts detected during reconcile");
}
// 6. Post to parent
await vms.post({ sessionId: session.sessionId });
// 7. Stop editing
await vms.stopEditing({
sessionId: session.sessionId,
saveEdits: true
});
console.log("Edits posted successfully");
} finally {
// 8. Switch back to default and delete temp version
featureLayer.gdbVersion = vms.defaultVersionName;
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);
});
Edit Related Records in Form
const editor = new Editor({
view: view,
layerInfos: [{
layer: parentLayer,
// Include related tables
relatedTableInfos: [{
layer: relatedTable,
addEnabled: true,
updateEnabled: true,
deleteEnabled: true
}]
}]
});
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",
validationExpression: {
expression: `
var email = $feature.email;
return IIf(Find("@", email) > 0, true, { valid: false, errorMessage: "Invalid email" });
`
}
}]
}
});
// Check validity before submit
if (form.valid) {
// Submit
} else {
// Show validation errors
}
Complete Example
<!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; }
#formPanel { position: absolute; top: 10px; right: 10px; width: 300px; }
</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.
Common Pitfalls
-
Editing permissions: User must have edit permissions on the layer
-
Subtype field: Must match the subtype configuration in the service
-
Version locking: Branch versions may lock during editing sessions
-
Validation expressions: Must return true/false or error object
-
Related records: Require proper relationship configuration in service
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