How to Apply Edits to Several Features All At Once in JS API?

2186
4
04-14-2018 07:52 AM
by Anonymous User
Not applicable

Hey Team!

So, long story short, I'm creating an application that allows users to adopt road segments as the OOTB Esri option won't work for our needs. I've got to the point where users can click on the app, a form gets created and populated, they can input the necessary information, and apply the edits. However, as it stands, users can only select one feature at a time, which won't work. 

I need users to be able to select multiple features on a map, fill out one form, and have the fields in the form be applied as unique edits to each of the selected features. The testing code block is below:

<!DOCTYPE html>
<html>
     <head>
          <meta charset="utf-8">
          <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
          <title>Update FeatureLayer using applyEdits() - 4.6</title>
          <link rel="stylesheet" href="https://js.arcgis.com/4.6/esri/css/main.css">
          <script src="https://js.arcgis.com/4.6/"></script>
          <style>
               html,
               body,
               #viewDiv {
               padding: 0;
               margin: 0;
               height: 100%;
               width: 100%;
               }
               .editArea-container {
               background: #fff;
               font-family: "Avenir Next W00", "Helvetica Neue", Helvetica, Arial, sans-serif;
               line-height: 1.5em;
               overflow: auto;
               padding: 12px 15px;
               width: 300px;
               }
               .edit-button:hover,
               .edit-button:focus {
               background-color: #e4e4e4;
               color: #0079c1;
               cursor:pointer;
               }
               .inputInfo {
               font-size: 12px;
               height: 32px;
               margin-bottom: 6px;
               padding: 0 6px;
               width: 100%;
               }
               .list-heading {
               font-weight: normal;
               margin-top: 20px;
               margin-bottom: 10px;
               color: #323232;
               }
               .edit-button {
               font-size: 14px;
               height: 32px;
               margin-top: 10px;
               width: 100%;
               background-color: #0079c1;
               border: 1px solid #451F78;
               color: #FFFFFF;
               }
               .or-wrap {
               background-color: #e0e0e0;
               height: 1px;
               margin: 2em 0;
               overflow: visible;
               }
               .or-text {
               background: #fff;
               line-height: 0;
               padding: 0 1em;
               position: relative;
               top: -.75em;
               }
               input:invalid {
               border: 1px solid red;
               }
               input:valid {
               border: 1px solid green;
               }
          </style>
          <script>
               require([
                   "esri/Map",
                   "esri/views/MapView",
                   "esri/layers/Layer",
                   "esri/Graphic",
                   "esri/widgets/Expand",
                   "esri/widgets/Home",
          "esri/widgets/Search",
                   "esri/geometry/Extent",
                   "esri/Viewpoint",
                   "esri/core/watchUtils",
                   "dojo/on",
                   "dojo/dom",
                   "dojo/domReady!"
                 ],
                 function(
                   Map, MapView, Layer, Graphic, Expand,
                   Home, Search, Extent, Viewpoint, watchUtils,
                   on, dom
                 ) {
               
                   var featureLayer, editExpand;
               
                   // feature edit area domNodes
                   var editArea, attributeEditing, inputDescription, inputOrg, inputSign, inputName, inputAddress, inputCity, inputState, inputZip, inputPhone,
                     inputUserInfo, updateInstructionDiv;
               
                   var map = new Map({
                     basemap: "topo"
                   });

          var searchWidget = new Search({
            view: view
          });
               
                   // initial extent of the view and home button
                   var initialExtent = new Extent({
                     xmin: -9006905,
                     xmax: -8992793,
                     ymin: 4191052,
                     ymax: 4199068,
                     spatialReference: 102100
                   });
               
                   var view = new MapView({
                     container: "viewDiv",
                     map: map,
                     extent: initialExtent
                   });
               
                   // add an editable featurelayer from portal
                   Layer.fromPortalItem({
                       portalItem: { // autocasts as new PortalItem()
                         id: "821542fffc1c4fc6b38297d7b9df4567"
                       }
                     }).then(addLayer)
                     .otherwise(handleLayerLoadError);
               
                   setupEditing();
                   setupView();
               
                   function addLayer(lyr) {
                     featureLayer = lyr;
                     map.add(lyr);
                   }
               
                   function applyEdits(params) {
                     unselectFeature();
                     var promise = featureLayer.applyEdits(params);
                     editResultsHandler(promise);
                   }
               
                   // *****************************************************
                   // applyEdits promise resolved successfully
                   // query the newly created feature from the featurelayer
                   // set the editFeature object so that it can be used
                   // to update its features.
                   // *****************************************************
                   function editResultsHandler(promise) {
                     promise
                       .then(function(editsResult) {
                         var extractObjectId = function(result) {
                           return result.objectId;
                         };
               
                         // get the objectId of the newly added feature
                         if (editsResult.addFeatureResults.length > 0) {
                           var adds = editsResult.addFeatureResults.map(
                             extractObjectId);
                           newIncidentId = adds[0];
               
                           selectFeature(newIncidentId);
                         }
                       })
                       .otherwise(function(error) {
                         console.log("===============================================");
                         console.error("[ applyEdits ] FAILURE: ", error.code, error.name,
                           error.message);
                         console.log("error = ", error);
                       });
                   }
               
                   // *****************************************************
                   // listen to click event on the view
                   // 1. select if there is an intersecting feature
                   // 2. set the instance of editFeature
                   // 3. editFeature is the feature to update or delete
                   // *****************************************************
                   view.on("click", function(evt) {
                     unselectFeature();
                     view.hitTest(evt).then(function(response) {
                       if (response.results.length > 0 && response.results[0].graphic) {
               
                         var feature = response.results[0].graphic;
                         selectFeature(feature.attributes[featureLayer.objectIdField]);
               // This may be a potential problem area -- will not change until later
                         inputDescription.value = feature.attributes[
                           "FULL_NAME"];
                inputOrg.value = feature.attributes[
                  "Org_Name"];
                inputSign.value = feature.attributes[
                  "Sign_Verbage"];
                inputName.value = feature.attributes[
                  "ContactName"];
                inputAddress.value = feature.attributes[
                  "OrgAddress"];
                inputCity.value = feature.attributes[
                  "OrgCity"];
                inputState.value = feature.attributes[
                  "OrgState"];
                inputZip.value = feature.attributes[
                  "OrgZip"];
                inputPhone.value = feature.attributes[
                  "OrgPhone"];
                         inputUserInfo.value = feature.attributes[
                           "Email"];
                         attributeEditing.style.display = "block";
                         updateInstructionDiv.style.display = "none";
                       }
                     });
                   });
               
                   // *****************************************************
                   // select Feature function
                   // 1. Select the newly created feature on the view
                   // 2. or select an existing feature when user click on it
                   // 3. Symbolize the feature with cyan rectangle
                   // *****************************************************
                   function selectFeature(objectId) {
                     // symbol for the selected feature on the view
                     var selectionSymbol = {
                       type: "simple-marker", // autocasts as new SimpleMarkerSymbol()
                       color: [0, 0, 0, 0],
                       style: "square",
                       size: "40px",
                       outline: {
                         color: [0, 255, 255, 1],
                         width: "3px"
                       }
                     };
                     var query = featureLayer.createQuery();
                     query.where = featureLayer.objectIdField + " = " + objectId;
               
                     featureLayer.queryFeatures(query).then(function(results) {
                       if (results.features.length > 0) {
                         editFeature = results.features[0];
                         editFeature.symbol = selectionSymbol;
                         view.graphics.add(editFeature);
                       }
                     });
                   }
               
                   // *****************************************************
                   // hide attributes update and delete part when necessary
                   // *****************************************************
                   function unselectFeature() {
                     attributeEditing.style.display = "none";
                     updateInstructionDiv.style.display = "block";
               
                     inputDescription.value = null;
               inputOrg.value = null;
               inputSign.value = null;
               inputName.value = null;
               inputAddress.value = null;
               inputCity.value = null;
               inputState.value = null;
               inputZip.value = null;
               inputPhone.value = null;
                     inputUserInfo.value = null;
                     view.graphics.removeAll();
                   }
               
                   // *****************************************************
                   // add homeButton and expand widgets to UI
                   // *****************************************************
                   function setupView() {
                     // set home button view point to initial extent
                     var homeButton = new Home({
                       view: view,
                       viewpoint: new Viewpoint({
                         targetGeometry: initialExtent
                       })
                     });
                   //  view.ui.add(searchWidget, "top-left");
                    // view.ui.add(homeButton, "top-left");
               
                     // expand widget
                     editExpand = new Expand({
                       expandIconClass: "esri-icon-edit",
                       expandTooltip: "Expand Edit",
                       expanded: true,
                       view: view,
                       content: editArea
                     });
            
            view.ui.add([
              {
              component: searchWidget,
              position: "top-left",
              index: 0
              }, {
              component: homeButton,
              position: "top-left"
              }, {
              component: editExpand,
              position: "top-right",
              }
            ]);
                   }
                   
               
                   // *****************************************************
                   // set up for editing
                   // *****************************************************
                   function setupEditing() {
                     // input boxes for the attribute editing
                     editArea = dom.byId("editArea");
                     updateInstructionDiv = dom.byId("updateInstructionDiv");
                     attributeEditing = dom.byId("featureUpdateDiv");
                     inputDescription = dom.byId("inputDescription");
               inputOrg = dom.byId("inputOrg");
               inputSign = dom.byId("inputSign");
               inputName = dom.byId("inputName");
               inputAddress = dom.byId("inputAddress");
               inputCity = dom.byId("inputCity");
               inputState = dom.byId("inputState");
               inputZip = dom.byId("inputZip");
               inputPhone = dom.byId("inputPhone");
                     inputUserInfo = dom.byId("inputUserInfo");
               
                     // *****************************************************
                     // btnUpdate click event
                     // update attributes of selected feature
                     // *****************************************************
                     on(dom.byId("btnUpdate"), "click", function(evt) {
                       if (editFeature) {
                         editFeature.attributes["FULL_NAME"] = inputDescription.value;
                //editFeature.attributes["Org_Name"] = inputOrg.value;
                //editFeature.attributes["Sign_Verbage"] = inputSign.value;
                //editFeature.attributes["ContactName"] = inputName.value;
                //editFeature.attributes["OrgAddress"] = inputAddress.value;
                //editFeature.attributes["OrgCity"] = inputCity.value;
                //editFeature.attributes["OrgState"] = inputState.value;
                //editFeature.attributes["OrgZip"] = inputZip.value;
                //editFeature.attributes["OrgPhone"] = inputPhone.value;
                //editFeature.attributes["Email"] = inputUserInfo.value;
               
                         var edits = {
                           updateFeatures: [editFeature]
                         };
               
                         applyEdits(edits);
                       }
                     });
               
                     // *****************************************************
                     // btnAddFeature click event
                     // create a new feature at the click location
                     // *****************************************************
                     on(dom.byId("btnAddFeature"), "click", function() {
                       unselectFeature();
                       on.once(view, "click", function(event) {
                         event.stopPropagation();
               
                         if (event.mapPoint) {
                           point = event.mapPoint.clone();
                           point.z = undefined;
                           point.hasZ = false;
               
                           newIncident = new Graphic({
                             geometry: point,
                             attributes: {}
                           });
               
                           var edits = {
                             addFeatures: [newIncident]
                           };
               
                           applyEdits(edits);
               
                           // ui changes in response to creating a new feature
                           // display feature update and delete portion of the edit area
                           attributeEditing.style.display = "block";
                           updateInstructionDiv.style.display = "none";
                           dom.byId("viewDiv").style.cursor = "auto";
                         }
                         else {
                           console.error("event.mapPoint is not defined");
                         }
                       });
               
                       // change the view's mouse cursor once user selects
                       // a new incident type to create
                       dom.byId("viewDiv").style.cursor = "crosshair";
                       editArea.style.cursor = "auto";
                     });
               
                     // *****************************************************
                     // delete button click event. ApplyEdits is called
                     // with the selected feature to be deleted
                     // *****************************************************
                     on(dom.byId("btnDelete"), "click", function() {
                       window.open("https://survey123.arcgis.com/share/5e8d603ed41a4e49b99c420a32a9e8e7")
                     });
               
                     // *****************************************************
                     // watch for view LOD change. Display Feature editing
                     // area when view.zoom level is 14 or higher
                     // otherwise hide the feature editing area
                     // *****************************************************
                     view.when(function() {
                       watchUtils.whenTrue(view, "stationary", function() {
                         if (editExpand) {
                           if (view.zoom <= 12) {
                             editExpand.domNode.style.display = "none";
                           }
                           else {
                             editExpand.domNode.style.display = "block";
                           }
                         }
                       });
                     });
                   }
               
                   function handleLayerLoadError(err) {
                     console.log("Layer failed to load: ", err);
                   }
                 });
          </script>
     </head>
     <body>
          <div id="editArea" class="editArea-container">
               <div id="addFeatureDiv" style="overflow:auto">
                    <h3 class="list-heading">Charlotte's Adopt A City Street</h3>
                    <ul style="font-size: 13px; padding-left: 1.5em;">
                         <li>To Adopt Available Streets, click the 'Start Adopting' Button</li>
                         <li>To Submit Your Cleanup Report, click the 'Submit Cleanup Report' Button</li>
                    </ul>
                    <input type="button" class="edit-button" value="Start Adopting" id="btnAddFeature">
               </div>
               <div id="updateInstructionDiv" style="text-align:center">
                    <p class="or-wrap"><span class="or-text">Or</span></p>
                    <input type="button" class="edit-button" value="Submit Cleanup Report" id="btnDelete">
               </div>
               <div id="featureUpdateDiv" style="display:none; margin-top: 1em">
                    <h3 class="list-heading">Enter Your Information Below</h3>
                    <div id="attributeArea">
                         <label for="inputDescription">Selected Streets:</label>
                         <input class="inputInfo" required disabled type="text" id="inputDescription" placeHolder="Streets Selected"><br>
                         <label for="inputOrg">Organization Name:</label>
                         <input class="inputInfo" required type="text" id="inputOrg" placeHolder="Enter organization name"><br>
                         <label for="inputSign">Adopt-A-Street Sign Text:</label>
                         <input class="inputInfo" required type="text" maxlength="34" id="inputSign" placeHolder="Enter the text you'd like on your sign"><br>
                         <label for="inputName">Name (First, Last):</label>
                         <input class="inputInfo" required type="text" id="inputName" placeHolder="Enter your name (first and last)"><br>
                         <label for="inputAddress">Street Address:</label>
                         <input class="inputInfo" required type="text" id="inputAddress" placeHolder="Enter your address"><br>
                         <label for="inputCity">City:</label>
                         <input class="inputInfo" required type="text" id="inputCity" placeHolder="Enter your city"><br>
                         <label for="inputState">State:</label>
                         <input class="inputInfo" required type="text" maxlength="2" id="inputState" placeHolder="Enter your state"><br>
                         <label for="inputZip">Zip Code:</label>
                         <input class="inputInfo" required type="number" maxlength="5" id="inputZip" placeHolder="Enter your zip code"><br>
                         <label for="inputPhone">Phone Number:</label>
                         <input class="inputInfo" required type="text" maxlength="15" id="inputPhone" placeHolder="Enter your phone number"><br>
                         <label for="inputUserInfo">Email Address:</label>
                         <input class="inputInfo" required type="email" name="email" id="inputUserInfo" pattern="[^ @]*@[^ @]*"
                              placeHolder="Enter email address"><br>
                         <input type="button" class="edit-button" value="Submit Adoption Request" id="btnUpdate">
                    </div>
                    <div id="deleteArea">
                    </div>
               </div>
          </div>
          <div id="viewDiv"></div>
     </body>
</html>
0 Kudos
4 Replies
KenBuja
MVP Esteemed Contributor

The way I've done it in my application (using JS 3.x) is to take a FeatureSet returned by the feature layer's selectFeatures method, run it through a loop to apply all the new attributes, and save those edits. I have a form where the user selects the attributes to be changed ('cboPriority', etc)

array.forEach(featureSet, function (feature) {
  feature.attributes.Priority = registry.byId('cboPriority').get("value");
  feature.attributes.Management = registry.byId('cboManagement').get("value");
  feature.attributes.Criteria = registry.byId('cboCriteria').get("value");
});

layerFeatureLayer.applyEdits(null, featureSet, null, function () { console.log("Features updated!"); }, function (error) { console.log("Features not updated! ", error); });‍‍‍‍‍‍‍
by Anonymous User
Not applicable

Is that application on github? I'd love to see how you accomplish this in the greater context of your application!

0 Kudos
KenBuja
MVP Esteemed Contributor

No, I haven't put it up on Github. The full site is here, but it requires an account to do the editing in the Prioritization tab.

0 Kudos
YugandharTilakBusam
New Contributor

n anyone had the sample code that should call applyEdits on a layer with multiple features in it. Thanks.

0 Kudos