csergent08

Adding an Editor to the Template

Blog Post created by csergent08 Champion on Jan 30, 2015

Today we are going to continue on and create the editor widget. If you would like to see what the code looks like before it's added, you can get it here. With the editor widget, you can allow staff to go out and enter data remotely on a mobile device using geolocation which is implemented in this application and the information immediately updates to the server. For me, the only downside to the editor widget is that you can only edit spatial data. I was hoping you would be able to edit data in non-spatial database tables as well. The new DataAdapterFeatureLayer widget, which you can read about under what's new in version 3.12 allows you to read data that has no spatial information, but does not allow you to edit the data. I still have hope for that in a future release. But with that, let's add the editor widget to our application. If you want to add it to yours, you can pretty much follow what I did here. The one nuance is that I have a "geocode an address" in my template app and the infoWindows conflict with the editor widget. The good news is that I have a work around built into my application that avoids this conflict. If you have infoWindows conflicting in your app, this may give you some ideas on how to create your own work around for your issue. But, let's add the editor widget to the mobile template.

 

First, I like to create a button to show the editor widget, because there are times the user may want to hide it while working on the map. I just provide a button to bring it back, so after the showPrinter button we add this code:

 

 

<!-- Show Editor Begin -->
            <div id="editor">
                <input type="image" id="showEditorWidget" src="images/edit.ico" alt="Editor" title="Editor" />
            </div>
            <!-- Show Editor End -->

 

Now it's time to create the editor panel. I have added a close button on the editor so it can be hidden as stated above. You can actually place this code anywhere on your page as we will use CSS to place it where we want it.

<!-- Editor Pane Begin -->
            <div id="templatePickerPane">
                <!-- Editor Header Begin -->
                <div id="panelHeader">
                  <p>Default Editor<input type="image"
                                          id="closeEditor"
                                          src="images/close.ico"
                                          alt="Close Editor"
                                          title="Close Editor" />
                 </p>
                </div>
                <!-- Editor Header End -->
                
                <!-- Edito Begin -->
                <div id="editorDiv">
           
                </div>
                <!-- Editor End -->
          </div>
          <!-- Editor Pane End -->

 

Now it's time to style our editor. I like to put it about half way down the screen and against the right side. For placement and other styling, we add the following code:

/* Editor style begin */
           
    #templatePickerPane {
        width: 250px;
        height:260px;
        overflow: hidden;
        z-index:50;
        top:50%;  /* Half way down the screen */
        right:0%;/* Against the right side of the screen */
        position:absolute;
    }
           
    #panelHeader
    {
        background-color: #92A661;
        border-bottom: solid 1px #92A860;
        color: #FFF;
        font-size: 18px;
        height: 50px;
        line-height: 22px;
        margin: 0;
        overflow: hidden;
        padding: 10px 10px 10px 10px;
               
    }
           
                       
    #editorDiv
    {
        background-color:White;
        padding:10px;
    }


             
        .esriEditor .templatePicker
    {
        padding-bottom: 5px;
        padding-top: 5px;
        height: 120px;
        border-radius: 0px 0px 4px 4px;
        border: solid 1px #92A661;
    }


        .dj_ie .infowindow .window .top .right .user .content, .dj_ie .simpleInfoWindow .content
    {
        position: relative;
    }
           
   
           
           
    #closeEditor
    {
        position:absolute;
        right:5%;
    }
    
    
   
           
    #editor
    {
        height:30px;
        width:30px;
        position:absolute;
        top:320px;
        left:20px;
        z-index:50;
        padding: 5px 6px 6px 6px;
        background-color:#f9f8f8;
        border-radius:5px;
        visibility:hidden;
    }
    
    
     /* Editor Style End */
     
     /* Style tool buttons on hover */
     #showTools:hover, #hideTools:hover, #printer:hover, #editor:hover
     {
         background:#eee;
     }

 

Moving on to JavaScript, we need to add our declarations. I have included all declarations and variables that you need up to this point in this excerpt:

var map;
var editorWidget = null;
var featureLayerInfos;
var graphic;
var currLocation;
var watchId;
var pt;
var app = {};
// Get references to modules to be used
require(["esri/map",                                // mapSection
         "esri/config",                             // The default values for all JS API configuration options. 


         "esri/Color",  // measurementDiv


         "esri/dijit/editing/Editor",           // Editor
         "esri/dijit/Geocoder",                     // search
         "esri/dijit/HomeButton",                   // homeButton
         "esri/dijit/LocateButton",                 // locateButton
         "esri/dijit/Measurement", // measurementDiv
         "esri/dijit/OverviewMap", // Overview Map
         "esri/dijit/Scalebar",  // Scalebar


         "esri/geometry/Extent", // The minimum and maximum X- and Y- coordinates of a bounding box. Used to set custom extent
         "esri/geometry/Point",
         "esri/geometry/screenUtils", // search


         "esri/graphic", // search


         "esri/IdentityManager", // editor


         "esri/layers/ArcGISDynamicMapServiceLayer",
         "esri/layers/ArcGISTiledMapServiceLayer",
         "esri/layers/LayerDrawingOptions", // measurementDiv
         "esri/layers/FeatureLayer",


         "esri/renderers/SimpleRenderer", // measurementDiv


         "esri/SnappingManager", // measurementDiv    -add snapping capability


         "esri/sniff", // measurementDiv


         "esri/SpatialReference",  // editor


         "esri/symbols/SimpleFillSymbol", // measurementDiv
         "esri/symbols/SimpleLineSymbol", // measurementDiv
         "esri/symbols/SimpleMarkerSymbol", // search


         "esri/tasks/GeometryService",    // Represents a geometry service resource exposed by the ArcGIS Server REST API.
         "esri/tasks/PrintTask",          // printer
         "esri/tasks/PrintParameters",    // printer
         "esri/tasks/PrintTemplate",      // printer
         "esri/tasks/ProjectParameters",  // editor


         "esri/toolbars/draw",


         "dojo/_base/array",
         "dojo/_base/Color",                    // search
         "dojo/dom",                            // It is used for code like - dom.byId("someNode")
         "dojo/dom-construct",                  // search
         "dojo/keys",
         "dojo/on",                             // This module is used based on an even such as on("click")
         "dojo/parser",                         // The Dojo Parser is an optional module.
         "dojo/query",                      // search
         "dojo/i18n!esri/nls/jsapi",
         "dojo/dnd/Moveable",


         "dijit/layout/BorderContainer",
         "dijit/layout/ContentPane",
         "dijit/TitlePane",
         "dijit/form/CheckBox",
         "dojo/domReady!"],    // An AMD loaded plugin that will wait until the DOM has finished loading before returning.


// Set variables to be used with references (write variables and references in the same order and be careful of typos on your references)
         function (Map, esriConfig, Color,
                   Editor, Geocoder, HomeButton,
                   LocateButton, Measurement,
                   OverviewMap, Scalebar, Extent,
                   Point, screenUtils, Graphic,
                   IdentityManager, ArcGISDynamicMapServiceLayer, ArcGISTiledMapServiceLayer,
                   LayerDrawingOptions, FeatureLayer, SimpleRenderer,
                   SnappingManager, has, SpatialReference,
                   SimpleFillSymbol, SimpleLineSymbol, SimpleMarkerSymbol,
                   GeometryService, PrintTask, PrintParameters,
                   PrintTemplate, ProjectParameters, Draw,
                   arrayUtils, Color, dom,
                   domConstruct, keys, on,
                   parser, query, i18n,
                   Moveable) {

I like to only have three variables per line for readability. After the mapSection, update the code. In my first line, its says whenever the feature layer has been added initEditing fires, meaning I can edit right away.

 

Add the starts editing after creating the mapSection

// Starts initEditing after the feature layer(s) have been added
             map.on("layers-add-result", initEditing);

 

So that means we need to add a feature layer. Add a point feature layer. Set the layer in MODE_ONDEMAND as I only want the features that would be visible in my current extent to load and then I add all fields. With that we can load the layer.

 // add point feature layer for editing
             var pointFeatureLayer = new FeatureLayer("http://maps.decaturil.gov/arcgis/rest/services/testSecure/FeatureServer/0", {
                 mode: FeatureLayer.MODE_ONDEMAND,
                 outFields: ["*"]
             });
             map.addLayers([pointFeatureLayer]);

 

In this block, once the feature layers has been added as your recall, the initEditing event fires. Here we define the editor widget size and then fire the createEditor function. In the createEdtior function, we have added the editor widget's toolbar and also set the max undo to 20. You may notice the destroyEditor function. We're not calling that yet, but it's very important.

 

// Editor Widget Begin 
             // settings for the editor widget
             function initEditing(event) {
                 // sizes the edit window
                 map.infoWindow.resize(400, 300);
                 featureLayerInfos = arrayUtils.map(event.layers, function (layer) {
                     return {
                         "featureLayer": layer.layer
                     };
                 });


                 createEditor();
                 var options = {
                     snapKey: keys.copyKey
                 };
                 map.enableSnapping(options);
             }




             function createEditor() {
                 if (editorWidget) {
                     return;
                 }
                 var settings = {
                     map: map,
                     layerInfos: featureLayerInfos,
                     toolbarVisible: true,
                     enableUndoRedo: true,
                     maxUndoOperations: 20
                 };
                 var params = {
                     settings: settings
                 };
                 editorWidget = new Editor(params, domConstruct.create("div"));
                 domConstruct.place(editorWidget.domNode, "editorDiv");


                 editorWidget.startup();






             }


             function destroyEditor() {
                 if (editorWidget) {
                     editorWidget.destroy();
                     editorWidget = null;
                 }
             }
             // Editor widget ends

 

Here is where the conflict arises. The editor and the showLocation event both produce infoWindows and this is a conflict, so you may get the wrong infoWindow when you run one of the tools. What do you do? We refer to that function, destroyEditor and get rid of the editor when we run the showLocation and then createEditor. This removes the conflict from the application. This was driving me nuts. Thankfully Kelly Hutchins was able to figure this creative solution out for me.

 

 

function showLocation(evt) {
                 map.graphics.clear();
                 var point = evt.result.feature.geometry;
                 var symbol = new SimpleMarkerSymbol()
                                .setStyle("square")
                                .setColor([255, 0, 0, 0.5]);
                 var graphic = new Graphic(point, symbol);
                 map.graphics.add(graphic);


                 map.infoWindow.setTitle("Search Result");
                 map.infoWindow.setContent(evt.result.name);
                 map.infoWindow.show(evt.result.feature.geometry);
                 map.infoWindow.on('hide', function () {
                     map.graphics.remove(graphic);
                     destroyEditor();
                     createEditor();
                 });

 

 

The next two lines just allow the user to control over whether the editor is visible or not at any given time.

document.getElementById("editor").style.visibility = 'visible';

 

Hide tools add this line:

document.getElementById("editor").style.visibility = 'hidden';

 

And the last thing I like to add to my widgets is to make them draggable. The jQuery function allows you to drag the editor pane possibly out of the way of where you might want to edit. And even though this is not  jQuery mobile, you can still use it on mobile devices as I use jqueryPunch to make my jQuery code work on mobile devices.

 

// Hide editor
             on(dom.byId("closeEditor"), "click", function () {
                 document.getElementById("templatePickerPane").style.visibility = 'hidden';
             });




             // Show Editor
             on(dom.byId("showEditorWidget"), "click", function () {
                 document.getElementById("templatePickerPane").style.visibility = 'visible';


             });




             // Allow editor to move with mouse or finger
             jQuery(function () {
                 jQuery("#templatePickerPane").draggable({ containment: "window" });
             });

 

There is just one last item to add to make this mobile template complete, which allowing your users to contact you while in the field, but if you want the fully functioning code up to this point, grab it from here anytime.

Outcomes