applyEdits() with a button rather than create-complete JS API 4.9

873
3
Jump to solution
11-19-2018 06:19 PM
SimonWebster
Occasional Contributor

Hi, 

I've successfully managed to create new polygon features in a featurelayer using sketch geometries applyEdits() when the user finishes drawing. This, however, has some issues from a usability point of view - in that if my user has made a mistake they cannot edit the polygon before the rest of the form on the page is submitted. 

What I'd like to do is either 

a) submit the polygon when the page on the form is submitted (preferable), or;

b) work out how to delete the polygon from the feature service using the drawing that's still on the screen

The function I am successfully using: 

           // Listen to create-complete event to add a newly created graphic to view
            sketchViewModel.on("create-complete", addGraphic);

            // Listen the sketchViewModel's update-complete and update-cancel events
            sketchViewModel.on("update-complete", updateGraphic);
            sketchViewModel.on("update-cancel", updateGraphic);

            // called when sketchViewModel's create-complete event is fired.
            function addGraphic(event) {
                // Create a new graphic and set its geometry to
                // `create-complete` event geometry.
                var varCreatedByUser = document.querySelector('#Created_By').value;
                var varCreatedDate = document.querySelector('#Upload_Date').value;
                var varActivityType = document.querySelector('#Activity_Type').value;
                var varActivityName = document.querySelector('#ActivityName').value;
                var varOCBYear = document.querySelector('#Year').value;
                const graphic = new Graphic({
                    geometry: event.geometry,
                    symbol: sketchViewModel.graphic.symbol,
                    attributes: {
                        CreatedByUser: varCreatedByUser,
                        CreatedDate: varCreatedDate,
                        ActivityType: varActivityType,
                        ActivityName: varActivityName,
                        OCBYear: varOCBYear
                    }
                });

                graphicsLayer.add(graphic);

               const edits = { //Fire the addFeatures function using the completed graphic
                   addFeatures: [graphic]
                };

                console.log(currentSketch);
                applyEdits(edits);

                document.getElementById("polygonButton").disabled = true; //disable new feature button to prevent duplicates

                console.log(graphic);
            }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

What I have tried so far:

Option a)

Watching my form submit button for an onclick action and attempting to grab the graphic in the same way that the function addGraphic(event) function does. Not sure if this is a great idea as I'm also sending a form that causes a page change.. 
I assume this one fails because I'm not passing the object in via the event, though I'm not really sure. 

            //Try to catch the form submit function
            document.getElementById("btnFormSubmit").onclick = function () {
                var varCreatedByUser = document.querySelector('#Created_By').value;
                var varCreatedDate = document.querySelector('#Upload_Date').value;
                var varActivityType = document.querySelector('#Activity_Type').value;
                var varActivityName = document.querySelector('#ActivityName').value;
                var varOCBYear = document.querySelector('#Year').value;
                const graphic = new Graphic({
                    geometry: event.geometry,
                    symbol: sketchViewModel.graphic.symbol,
                    attributes: {
                        CreatedByUser: varCreatedByUser,
                        CreatedDate: varCreatedDate,
                        ActivityType: varActivityType,
                        ActivityName: varActivityName,
                        OCBYear: varOCBYear
                    }
                });
                //graphicsLayer.add(graphic)
                const edits = { //Fire the addFeatures function using the completed graphic
                    addFeatures: [graphic]
                };
                applyEdits(edits);
            }

Option b)

I also tried for option b) by allowing the graphic to be submitted and then allowing the user to delete the feature and start again. I suspect I'm missing quite a bit here - like I'm sure I probably need to be collecting the object ID of the feature from the server after submission, storing that in a global variable, and then on delete passing that Object ID back + clear the map. I'm just not sure how, and I have not found a reference to this that seems like it would work. I've also tried referencing deleteFeatures: [graphic], but I'm assuming it's out of scope since its created in the create function. I'm not really sure about that being the right line of thought either. 

           // *****************************************************
            // Delete button click event. ApplyEdits is called
            // with the selected feature to be deleted.
            // *****************************************************
            document.getElementById("btnDelete").onclick = function () {
                // setup the applyEdits parameter with deletes.
                const edits = {
                    deleteFeatures: [editFeature]
                };
                console.log(editFeature);
                applyEdits(edits);
                document.getElementById("polygonButton").enabled = true; //disable new feature button to prevent duplicates
            }

Any ideas?

Thanks in advance

0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Esteemed Contributor

Simon,

   When the "create-complete" Method is called you need to store the event.geometry in a global variable then on your btnFormSubmit click method you have

const graphic = new Graphic({
  geometry: gVarGeomtry,

View solution in original post

0 Kudos
3 Replies
RobertScheitlin__GISP
MVP Esteemed Contributor

Simon,

   When the "create-complete" Method is called you need to store the event.geometry in a global variable then on your btnFormSubmit click method you have

const graphic = new Graphic({
  geometry: gVarGeomtry,
0 Kudos
SimonWebster
Occasional Contributor

Thanks Robert, 

This was the clue I needed. 

For future reference:

Declare a global variable

        var gEventGeometry;

          //Placed right above view.when(function {}) so it has almost global scope ‍‍‍

Add the graphic to the map, and load the global variable with the geometry

function addGraphic(event) {
                // Create a new graphic and set its geometry to
                // `create-complete` event geometry.

                //Apply event.geometry to a variable created globally, or up-scope
                gEventGeometry = event.geometry;

                //Confirm that the geometry is being added to the variable
                console.log(gEventGeometry);

                //Create a graphic array that can be added to the map, contains geometry
                //and symbol
                const graphic = new Graphic({
                    geometry: event.geometry,
                    symbol: sketchViewModel.graphic.symbol,
                });
                
                //Add the array as an overlay so the user can see what they drew
                graphicsLayer.add(graphic);

                //Confirm that the graphic is being added by printing to console, not
                //actually necessary
                console.log(graphic);
            }

Add an event watcher onclick() function, apply the edits when the form is submitted.

            //Try to catch the form submit function
            document.getElementById("btnSubmitForm").onclick = function () {
                //Creating variables because my polygon also collects form values. 
                //The variables are collected with jquery. You could use getElementByID
                //The variables are then added as attributes
                var varCreatedByUser = document.querySelector('#Created_By').value;
                var varCreatedDate = document.querySelector('#Upload_Date').value;
                var varActivityType = document.querySelector('#Activity_Type').value;
                var varActivityName = document.querySelector('#ActivityName').value;
                var varOCBYear = document.querySelector('#Year').value;
                //Create a graphic, geometry comes from our global variable we loaded
                //in the last step (gEventGeometry)
                const graphic = new Graphic({
                    geometry: gEventGeometry,
                    symbol: sketchViewModel.graphic.symbol,
                    attributes: {
                        CreatedByUser: varCreatedByUser,
                        CreatedDate: varCreatedDate,
                        ActivityType: varActivityType,
                        ActivityName: varActivityName,
                        OCBYear: varOCBYear
                    }
                });

                //Load the constant 'edits' with the completed graphic
                const edits = { //Fire the addFeatures function using the completed
                                //graphic
                    addFeatures: [graphic]
                };

                //Send the results to the editable service
                applyEdits(edits);
            }
0 Kudos
SimonWebster
Occasional Contributor

It probably should be noted that the code I pasted above will result in a race condition. The code below solves the race condition.

In this scenario I'm specifically submitting a lengthy text based form, and a new polygon from a draw event. I want the form to be submitted to a database somewhere, and the feature service to be updated with my polygon.

The below code is modified to watch for a return from the editable feature service before proceeding with the form submit. Whilst I have not shown it in the code, this does mean you should validate your form with client side javascript so that any submissions are guaranteed to pass through both the feature service submit, and the form submit (you should also be using server side validation, of course).

I've left in a line where we load the returned objectID into a const. You could, in theory, add another line where you update a hidden field in the form with the const straight after this line so that your database recieving the form is linked to the feature layer and table by an objectID. This would also allow you to add an edit page later on where the specific feature could be zoomed to and shown, together with the forms edit page.

Your form must have a name. The button should not be a submit button, but rather just a type="button" as we will catch it later.

In your html

<form action="/do/stuff" id="frmMitActs" method="post" name="frmMitActs">
<!-- Our form elements, like input boxes and dropdowns go here -->        
<input type="button" value="Create" class="btn btn-default" id="btnSubmitForm" />
</form>‍‍‍‍

In the js used to generate your map, at the end of: view.when(function () {

//Try to catch the form submit function
            //If caught we:
            //1. Load some of our selected form elements to variables
            //2. construct the graphic const with geometry, symbol, and attributes as an object
            document.getElementById("btnSubmitForm").onclick = function () {
                console.log("btnSubmitForm successfully triggered");
                var varCreatedByUser = document.querySelector('#Created_By').value;
                var varCreatedDate = document.querySelector('#Upload_Date').value;
                var varActivityType = document.querySelector('#Activity_Type').value;
                var varActivityName = document.querySelector('#ActivityName').value;
                var varOCBYear = document.querySelector('#Year').value;
                const graphic = new Graphic({
                    geometry: gEventGeometry,
                    symbol: sketchViewModel.graphic.symbol,
                    attributes: {
                        CreatedByUser: varCreatedByUser,
                        CreatedDate: varCreatedDate,
                        ActivityType: varActivityType,
                        ActivityName: varActivityName,
                        OCBYear: varOCBYear
                    }
                });
                //graphicsLayer.add(graphic)
                const edits = { //Fire the addFeatures function using the completed graphic
                    addFeatures: [graphic]
                };
                console.log("Added the const named 'edits' and successfully added the graphic to the const");
                featureLayer.applyEdits(edits).then(function (editsResult) {
                    // Get the objectId of the newly added feature.
                    // Call selectFeature function to highlight the new feature.
                    //alert(editsResult.addFeatureResults.length);
                    if (editsResult.addFeatureResults.length > 0) { //check if return is > 0
                        const objectId = editsResult.addFeatureResults[0].objectId; //if it is, return our new objectID. This could be used to reference the mapservice object in the Activities db in future 
                        //alert(objectId);
                        console.log("Submitted polygon successfully");
                        //selectFeature(objectId);
                        document.getElementById("frmMitActs").submit(); //Thus, if the map server submission was successful, submit the form
                        console.log("Submitted form successfully");
                    }
                })
                    .catch(function (error) {
                        console.log("===============================================");
                        console.error("[ applyEdits ] FAILURE: ", error.code, error.name,
                            error.message);
                        console.log("error = ", error);
                    });;
            }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos