Editing features including subtypes, feature forms, versioning, and configurable editors. Use for data entry workflows and geodatabase version management.
53
60%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Fix and improve this skill with Tessl
tessl review fix ./contexts/4.34/skills/arcgis-editing/SKILL.mdUse this skill for 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.
<arcgis-map item-id="YOUR_EDITABLE_WEBMAP_ID">
<arcgis-editor slot="top-right"></arcgis-editor>
</arcgis-map>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");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]
});
}
});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" }
]
}
]
};const formTemplate = {
elements: [
{
type: "field",
fieldName: "type"
},
{
type: "field",
fieldName: "subtype",
visibilityExpression: "type-requires-subtype"
}
],
expressionInfos: [{
name: "type-requires-subtype",
expression: "$feature.type == 'complex'"
}]
};// 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);
});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
});const editor = new Editor({
view: view,
layerInfos: [{
layer: subtypeGroupLayer,
// Editor automatically handles subtypes
addEnabled: true,
updateEnabled: true
}]
});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 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 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 a version (must be owner or admin)
await vms.deleteVersion({
versionName: "sde.MyEditVersion"
});// Modify version properties
await vms.alterVersion({
versionName: "sde.MyEditVersion",
description: "Updated description",
access: "protected", // Change access level
ownerName: "newOwner" // Transfer ownership (admin only)
});// 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();
}
});// 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
});// 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");
}// 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
});
}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
});
}
}const relatedRecords = await layer.queryRelatedFeatures({
outFields: ["*"],
relationshipId: 0,
objectIds: [selectedFeature.attributes.OBJECTID]
});
relatedRecords[selectedObjectId].features.forEach(related => {
console.log("Related:", related.attributes);
});const editor = new Editor({
view: view,
layerInfos: [{
layer: featureLayer,
attachmentsOnCreateEnabled: true,
attachmentsOnUpdateEnabled: true
}]
});// 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]);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
}<!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><!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>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.
| Component | Purpose |
|---|---|
arcgis-version-management | Manage and switch between geodatabase versions |
widgets-editor-configurable - Configurable Editor widgetwidgets-editor-subtypes - Editing with subtype group layersediting-featureform-fieldvisibility - Controlling field visibility in formschanging-version - Switching geodatabase versionsEditing 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 editsImpact: 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
d84407b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.