<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Advanced Attribute Editing | Sample | ArcGIS Maps SDK for JavaScript</title>
<link rel="stylesheet" href="https://js.arcgis.com/5.0/esri/themes/light/main.css" />
<!-- Load the ArcGIS Maps SDK for JavaScript from CDN -->
<script type="module" src="https://js.arcgis.com/5.0/"></script>
/* replaces esri-widget--panel */
const [Map, MapView, FeatureForm, FeatureLayer, FieldElement, GroupElement] =
"@arcgis/core/views/MapView.js",
"@arcgis/core/widgets/FeatureForm.js",
"@arcgis/core/layers/FeatureLayer.js",
"@arcgis/core/form/elements/FieldElement.js",
"@arcgis/core/form/elements/GroupElement.js",
let highlight, editFeature;
const featureLayer = new FeatureLayer({
id: "1e6a26701cd94ca1880e6fc9c908f410", // Citizen problems portal layer
const view = new MapView({
center: [-88.149, 41.773],
// Create field and group elements to add to form template
// Create editable expression
const fieldStatusEditableExpressionInfo = {
name: "field-status-editable",
// Individual field elements to display
const fieldStatus = new FieldElement({
editableExpression: "field-status-editable",
description: "E.g. submitted, received, in progress, or completed.",
// Create editable expression
const fieldResolutionEditableExpressionInfo = {
name: "field-resolution-editable",
const fieldResolutionExpressionInfo = {
name: "field-resolution",
expression: "($feature.status == 'Completed') && (!(IsEmpty($feature.resolution)))",
const fieldResolution = new FieldElement({
editableExpression: "field-resolution-editable",
description: "Resolution if status is set to Completed",
visibilityExpression: "field-resolution",
// The following field elements will display within the first group element
const fieldCat = new FieldElement({
const categoryExpressionInfos = [
name: "category-AnimalProbType",
expression: "$feature.category == 'Animal'",
name: "category-BlightProbType",
expression: "$feature.category == 'Blight'",
name: "category-HealthProbType",
expression: "$feature.category == 'Health'",
name: "category-LanUseProbType",
expression: "$feature.category == 'Land Use'",
name: "category-ParktreeProbType",
expression: "$feature.category == 'Park/Tree'",
name: "category-RoadProbType",
expression: "$feature.category == 'Road'",
name: "category-SnowIceProbType",
expression: "$feature.category == 'Snow/Ice'",
name: "category-TrashProbType",
expression: "$feature.category == 'Trash'",
name: "category-UtiltiesProbType",
expression: "$feature.category == 'Utility'",
const fieldAnimal = new FieldElement({
fieldName: "AnimalProbType",
label: "Animal problem type",
visibilityExpression: "category-AnimalProbType",
description: "E.g. barking dog, bite, etc.",
const fieldBlight = new FieldElement({
fieldName: "BlightProbType",
label: "Blight problem type",
description: "E.g. graffiti, abandoned vehicle, etc.",
visibilityExpression: "category-BlightProbType",
const fieldHealth = new FieldElement({
fieldName: "HealthProbType",
label: "Health problem type",
description: "E.g. food poisoning, mosquito, etc.",
visibilityExpression: "category-HealthProbType",
const fieldLandUse = new FieldElement({
fieldName: "LanUseProbType",
label: "Land Use problem type",
description: "E.g. illegal parking, work without permit, etc.",
visibilityExpression: "category-LanUseProbType",
const fieldParkTree = new FieldElement({
fieldName: "ParktreeProbType",
label: "Park/Tree problem type",
description: "E.g. unsafe tree, light out, etc.",
visibilityExpression: "category-ParktreeProbType",
const fieldRoad = new FieldElement({
fieldName: "RoadProbType",
label: "Road problem type",
description: "E.g. pothole, sidewalk damage, etc.",
visibilityExpression: "category-RoadProbType",
const fieldSnowIce = new FieldElement({
fieldName: "SnowIceProbType",
label: "Snow/ice problem type",
description: "E.g. road not cleared, salt or sand required, etc.",
visibilityExpression: "category-SnowIceProbType",
const fieldTrash = new FieldElement({
fieldName: "TrashProbType",
label: "Trash problem type",
description: "E.g. bulk item pickup, recycling pickup, etc.",
visibilityExpression: "category-TrashProbType",
const fieldUtilities = new FieldElement({
fieldName: "UtiltiesProbType",
label: "Utility problem type",
description: "E.g. storm drain clog, water leak, etc.",
visibilityExpression: "category-UtiltiesProbType",
const fieldDetailsExpressionInfo = {
($feature.category == 'Animal' && (!IsEmpty($feature.AnimalProbType))) ||
($feature.category == 'Blight' && (!IsEmpty($feature.BlightProbType))) ||
($feature.category == 'Health' && (!IsEmpty($feature.HealthProbType))) ||
($feature.category == 'Land Use' && (!IsEmpty($feature.LanUseProbType))) ||
($feature.category == 'Park/Tree' && (!IsEmpty($feature.ParktreeProbtype))) ||
($feature.category == 'Road' && (!IsEmpty($feature.RoadProbtype))) ||
($feature.category == 'Snow/Ice' && (!IsEmpty($feature.SnowIceProbtype))) ||
($feature.category == 'Trash' && (!IsEmpty($feature.TrashProbType))) ||
($feature.category == 'Utility' && (!IsEmpty($feature.UtiltiesProbType)))
const fieldDetails = new FieldElement({
description: "Enter additional details about the problem",
// If the category is a value and the sub categories are NOT NULL, show the details field
visibilityExpression: "field-details",
const groupUpdateExpressionInfo = {
expression: "($feature.status == 'Submitted') || ($feature.status == 'Received')",
// Create a group element and pass in the above field elements
const groupUpdate = new GroupElement({
description: "If work has not yet begun on issue, categorize and detail the problem",
visibilityExpression: "group-update",
// Only show the telephone and email fields if both first and last names are entered
const contactExpressionInfo = {
name: "contact-pocphone",
expression: "!IsEmpty($feature.pocfirstname) && !IsEmpty($feature.poclastname)",
// Create field elements to pass into second group element
const fieldFirst = new FieldElement({
fieldName: "pocfirstname",
const fieldLast = new FieldElement({
fieldName: "poclastname",
const fieldPhone = new FieldElement({
label: "Telephone number",
visibilityExpression: "contact-pocphone",
const fieldEmail = new FieldElement({
visibilityExpression: "contact-pocphone",
// Create second group element and pass in the above four field elements to it
const groupPOC = new GroupElement({
label: "Point of contact information",
description: "Who should we contact regarding this problem?",
elements: [fieldFirst, fieldLast, fieldPhone, fieldEmail],
// Add a new feature form and pass in the group and field elements from above
const form = new FeatureForm({
map: map, // required if using Arcade expressions using the $map global variable
groupDisplay: "sequential",
// Autocasts to FormTemplate
description: "Provide information to the questions below",
elements: [fieldStatus, fieldResolution, groupUpdate, groupPOC],
fieldResolutionExpressionInfo,
fieldResolutionEditableExpressionInfo,
fieldStatusEditableExpressionInfo,
...categoryExpressionInfos,
fieldDetailsExpressionInfo,
groupUpdateExpressionInfo,
view.on("click", (event) => {
// Unselect any currently selected features
// Listen for when the user clicks on the view
view.hitTest(event).then((response) => {
// If user selects a feature, select it
const results = response.results;
results[0].graphic.layer === featureLayer
selectFeature(response.results[0].graphic.getObjectId());
// Hide the form and show the info div
document.getElementById("update").classList.add("esri-hidden");
// Function to unselect features
function unselectFeature() {
// Highlight the clicked feature and display
// its attributes in the featureform.
function selectFeature(objectId) {
// query feature from the server
if (results.features.length > 0) {
editFeature = results.features[0];
// display the attributes of selected feature in the form
form.feature = editFeature;
// highlight the feature on the view
view.whenLayerView(editFeature.layer).then((layerView) => {
highlight = layerView.highlight(editFeature);
if (document.getElementById("update").classList.contains("esri-hidden")) {
document.getElementById("info").classList.add("esri-hidden");
document.getElementById("update").classList.remove("esri-hidden");
// Listen to the feature form's submit event.
form.on("submit", () => {
// Grab updated attributes from the form.
const updated = form.getValues();
// Loop through updated attributes and assign
// the updated values to feature attributes.
Object.keys(updated).forEach((name) => {
editFeature.attributes[name] = updated[name];
// Setup the applyEdits parameter with updates.
updateFeatures: [editFeature],
applyAttributeUpdates(edits);
// Call FeatureLayer.applyEdits() with specified params.
function applyAttributeUpdates(params) {
document.getElementById("btnUpdate").style.cursor = "progress";
// Get the objectId of the newly added feature.
// Call selectFeature function to highlight the new feature.
if (editsResult.addFeatureResults.length > 0) {
const objectId = editsResult.addFeatureResults[0].objectId;
document.getElementById("btnUpdate").style.cursor = "pointer";
console.log("===============================================");
console.error("[ applyEdits ] FAILURE: ", error.code, error.name, error.message);
console.log("error = ", error);
document.getElementById("btnUpdate").style.cursor = "pointer";
document.getElementById("btnUpdate").onclick = () => {
// Fires feature form's submit event.
view.ui.add("update", "top-right");
<div id="info" class="esri-widget">
<h3>Select a feature to begin editing</h3>
You will only be able to update an issue if work has not yet begun on it, e.g. status is
either 'Submitted' or 'Received'.
If work has already begun, you have the ability to update the contact information. If no
first and last names are provided, no option is given to enter a telephone number or
<div id="update" class="esri-widget esri-hidden">
<div id="form" class="scroller esri-component"></div>
<input type="button" class="esri-button" value="Update assessment" id="btnUpdate" />