Getting Started with Editing

Basic steps for working with Editing geographic features for offline.

Step 1: Fill in the basics

Add library references and build basic layout. Then test to make sure map loads.


                        
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
    <title>Offline Features</title>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" media="screen">
    <link rel="stylesheet" href="http://js.arcgis.com/3.11/esri/css/esri.css"">
    <link rel="stylesheet" href="widgets/css/modal-popup.css">
    <link rel="stylesheet" type="text/css" href="http://esri.github.io/bootstrap-map-js/src/css/bootstrapmap.css">
    <style>
        #mapDiv {
            min-height: 500px;
            max-height: 1000px;
        }

        #img-offline-indicator {
            padding: 8px;
            position: relative; float: right;
        }

        /* Override mod-popup default */
        .mod-popup-stop-input {color: black;}
        .span-pending {color: blue; padding-left: 1em;}
        .voffset20px { margin-top: 20px; }
        .floatRight { float: right}
    </style>

    <!-- Include a reference to offline.js which detects online/offline conditions -->
    <script src="../vendor/offline/offline.min.js"></script>
    <script>
        // Set how we pull in custom AMD modules
        var path = location.pathname.replace(/[^\/]+$/, '');
        var dojoConfig =
        {
            debug: true,
            packages:[
                {
                    name: "widgets",
                    location: path + "widgets/"
                }]
        }

        // Set the online/offline detection options.
        // More info at: http://github.hubspot.com/offline/docs/welcome/
        Offline.options = {
            checks: {
                image: {
                    url: function() {
                        return 'http://esri.github.io/offline-editor-js/tiny-image.png?_=' +
                                (Math.floor(Math.random() * 1000000000));
                    }
                },
                active: 'image'
            }
        }
    </script>

    <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
    <script src="http://js.arcgis.com/3.11/"></script>
</head>

<body>

<div class="container voffset20px">
    <div class="row">
        <div class="col-xs-8">
            <button class="btn btn-success" id="btn-online-offline">Go Offline</button>
            <span class="span-pending">Pending Edits <span id="span-pending-edits" class="badge">0</span></span>
        </div>
        <div class="col-xs-4">
            <button id="btn-state" class="btn btn-success btn-large floatRight">
                <span id="state-span" class="glyphicon glyphicon-link"> Up</span>
            </button>
        </div>
    </div>
    <div class="row voffset20px">
        <div class="col-xs-12">
            <div id="mapDiv"></div>
        </div>
    </div>
</div>
<!-- Stub for modal popup -->
<div id="modal-popup"></div>

<script>

require([
        "esri/map","esri/tasks/query",
        "dojo/on","dojo/parser", "esri/renderers/SimpleRenderer",
        "esri/symbols/SimpleMarkerSymbol","esri/Color",
        "widgets/popup","esri/layers/FeatureLayer",
        "//esri.github.com/bootstrap-map-js/src/js/bootstrapmap.js",
        "../dist/offline-edit-src.js",
        "dojo/domReady!"],
        function(Map,Query,on,parser,SimpleRenderer,SimpleMarkerSymbol,
            Color,ModalPopup,FeatureLayer,BootstrapMap){

            var map, popup, editsStore;
            var defaultSymbol;
            var offlineFeaturesManager;
            var btnOnlineOffline, btnState, pendingEdits;
            var imgOfflineIndicator;
            var closeBtn,saveBtn,deleteBtn,stopMainID,stopID,stopRoutes,stopNames;

            initMap();

            function initMap(){

                map = BootstrapMap.create("mapDiv",{
                    basemap: "topo",
                    center: [-104.99,39.75], // longitude, latitude
                    zoom: 15
                });
            }
        }
);

</script>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</body>
</html>

                        
                    
NOTE: Replace paths with your references. Or build your app in the /demo directory

Step 2: Configure modal popup

This initializes and configures the modal popup. Test to make sure map loads and popup displays.


                        
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
    <title>Offline Features</title>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" media="screen">
    <link rel="stylesheet" href="http://js.arcgis.com/3.11/esri/css/esri.css"">
    <link rel="stylesheet" href="../samples/widgets/modal/css/modal-popup.css">
    <link rel="stylesheet" type="text/css" href="http://esri.github.io/bootstrap-map-js/src/css/bootstrapmap.css">
    <style>
        #mapDiv {
            min-height: 500px;
            max-height: 1000px;
        }

        #img-offline-indicator {
            padding: 8px;
            position: relative; float: right;
        }

        /* Override mod-popup default */
        .mod-popup-stop-input {color: black;}
        .span-pending {color: blue; padding-left: 1em;}
        .voffset20px { margin-top: 20px; }
        .floatRight { float: right}
    </style>

    <!-- Include a reference to offline.js which detects online/offline conditions -->
    <script src="../vendor/offline/offline.min.js"></script>
    <script>
        // Set how we pull in custom AMD modules
        var path = location.pathname.replace(/[^\/]+$/, '');
        var dojoConfig =
        {
            debug: true,
            packages:[
                {
                    name: "widgets",
                    location: path + "../samples/widgets/"
                }]
        }

        // Set the online/offline detection options.
        // More info at: http://github.hubspot.com/offline/docs/welcome/
        Offline.options = {
            checks: {
                image: {
                    url: function() {
                        return 'http://esri.github.io/offline-editor-js/tiny-image.png?_=' +
                                (Math.floor(Math.random() * 1000000000));
                    }
                },
                active: 'image'
            }
        }
    </script>

    <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
    <script src="http://js.arcgis.com/3.11/"></script>
</head>

<body>

<div class="container voffset20px">
    <div class="row">
        <div class="col-xs-8">
            <button class="btn btn-success" id="btn-online-offline">Go Offline</button>
            <span class="span-pending">Pending Edits <span id="span-pending-edits" class="badge">0</span></span>
        </div>
        <div class="col-xs-4">
            <button id="btn-state" class="btn btn-success btn-large floatRight">
                <span id="state-span" class="glyphicon glyphicon-link"> Up</span>
            </button>
        </div>
    </div>
    <div class="row voffset20px">
        <div class="col-xs-12">
            <div id="mapDiv"></div>
        </div>
    </div>
</div>
<!-- Stub for modal popup -->
<div id="modal-popup"></div>

<script>

require([
        "esri/map","esri/tasks/query",
        "dojo/on","dojo/parser", "esri/renderers/SimpleRenderer",
        "esri/symbols/SimpleMarkerSymbol","esri/Color",
        "widgets/modal/popup","esri/layers/FeatureLayer",
        "//esri.github.com/bootstrap-map-js/src/js/bootstrapmap.js",
        "../dist/offline-edit-src.js",
        "dojo/domReady!"],
        function(Map,Query,on,parser,SimpleRenderer,SimpleMarkerSymbol,
             Color,ModalPopup,FeatureLayer,BootstrapMap) {

            var map, popup, editsStore;
            var defaultSymbol;
            var offlineFeaturesManager;
            var btnOnlineOffline, btnState, pendingEdits;
            var imgOfflineIndicator;
            var closeBtn,saveBtn,deleteBtn,stopMainID,stopID,stopRoutes,stopNames;

            initVars();
            initMap();

            function initMap(){

                map = BootstrapMap.create("mapDiv",{
                    basemap: "topo",
                    center: [-104.99,39.75], // longitude, latitude
                    zoom: 15
                });

                map.on("load",function(evt){
                    console.log("Map is loaded. Loading popup...")
                    window.setTimeout(function(){
                        popup.show();
                    },2000);
                });
            }


            function initVars(){

                editsStore = new O.esri.Edit.EditStore();
                popup = new ModalPopup({animation: true, animationDuration: 1},"modal-popup");
                popup.startup();

                parser.parse();

                // Check if browser state is online or offline
                Offline.check();
                Offline.on('up down', updateState );


                btnOnlineOffline = document.getElementById("btn-online-offline");
                imgOfflineIndicator = document.getElementById("state-span");
                btnState = document.getElementById("btn-state");
                pendingEdits = document.getElementById("span-pending-edits");

                // Modify symbol size based on screen size.
                // Bigger screens get smaller symbols. Smaller screens get larger symbols.
                var width = window.innerWidth
                        || document.documentElement.clientWidth
                        || document.body.clientWidth;

                var height = window.innerHeight
                        || document.documentElement.clientHeight
                        || document.body.clientHeight;

                if (height >= 768 || width >= 1024) {
                    defaultSymbol= new SimpleMarkerSymbol().setStyle(
                            SimpleMarkerSymbol.STYLE_DIAMOND).setColor(
                            new Color([255,0,0,0.5])).setSize(20);
                }
                else{
                    defaultSymbol= new SimpleMarkerSymbol().setStyle(
                            SimpleMarkerSymbol.STYLE_DIAMOND).setColor(
                            new Color([255,0,0,0.5])).setSize(35);
                }

                // Variables for modal popup
                closeBtn = document.getElementById("mod-popup-close-btn");
                saveBtn = document.getElementById("mod-popup-save-btn");
                deleteBtn = document.getElementById("mod-popup-delete-btn");
                stopMainID = document.getElementById("stop-main-id");
                stopID = document.getElementById("stop-id");
                stopRoutes = document.getElementById("stop-routes");
                stopNames = document.getElementById("stop-names");

            }

            function setModalPopupClickListeners(){
                closeBtn.onclick = function(evt){
                    hideModalPopup();
                }

                saveBtn.onclick = function(evt) {
                    //TO-DO
                }

            }

            function showModalPopup(graphic){
                popup.graphic = graphic; // assign graphic to modPopup as a property.
                popup.show();
            }

            function hideModalPopup(){
                popup.hide();
            }

            // Force feature service offline
            function goOnlineOffline(){

                if(btnOnlineOffline.innerHTML == "Go Offline"){
                    toggleStateUp(false);
                    console.log("Map is offline");
                }
                else{
                    toggleStateUp(true);
                    console.log("Map is online");
                }
            }

            // Set the UX to online or offline state
            function toggleStateUp(state){
                if(state){
                    btnOnlineOffline.innerHTML = "Go Offline";
                    offlineFeaturesManager.goOnline();
                    imgOfflineIndicator.className = "glyphicon glyphicon-link";
                    imgOfflineIndicator.innerHTML = " Up";
                    btnState.className = "btn btn-success btn-large floatRight";
                }
                else{
                    btnOnlineOffline.innerHTML = "Go Online";
                    offlineFeaturesManager.goOffline();
                    imgOfflineIndicator.className = "glyphicon glyphicon-thumbs-down";
                    imgOfflineIndicator.innerHTML = " Down";
                    btnState.className = "btn btn-danger btn-large floatRight";
                }
            }

            // Automatically set the feature service online or offline.
            function updateState(){
                if(Offline.state === 'up'){
                    toggleStateUp(true);
                }
                else{
                    toggleStateUp(false);
                }
            }
        }
);

</script>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</body>
</html>

                        
                    
NOTE: Replace paths with your references. Or build your app in the /demo directory

Step 3: Configure offline editing.

Enable the ability to store features (points, lines and polygons) while offline, and resync features when internet is restored.



                            

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
    <title>Offline Features</title>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" media="screen">
    <link rel="stylesheet" href="http://js.arcgis.com/3.11/esri/css/esri.css"">
    <link rel="stylesheet" href="../samples/widgets/modal/css/modal-popup.css">
    <link rel="stylesheet" type="text/css" href="http://esri.github.io/bootstrap-map-js/src/css/bootstrapmap.css">
    <style>
        #mapDiv {
            min-height: 500px;
            max-height: 1000px;
        }

        #img-offline-indicator {
            padding: 8px;
            position: relative; float: right;
        }

        /* Override mod-popup default */
        .mod-popup-stop-input {color: black;}
        .span-pending {color: blue; padding-left: 1em;}
        .voffset20px { margin-top: 20px; }
        .floatRight { float: right}
    </style>

    <!-- Include a reference to offline.js which detects online/offline conditions -->
    <script src="../vendor/offline/offline.min.js"></script>
    <script>
        // Set how we pull in custom AMD modules
        var path = location.pathname.replace(/[^\/]+$/, '');
        var dojoConfig =
        {
            debug: true,
            packages:[
                {
                    name: "widgets",
                    location: path + "../samples/widgets/"
                }]
        }

        // Set the online/offline detection options.
        // More info at: http://github.hubspot.com/offline/docs/welcome/
        Offline.options = {
            checks: {
                image: {
                    url: function() {
                        return 'http://esri.github.io/offline-editor-js/tiny-image.png?_=' +
                                (Math.floor(Math.random() * 1000000000));
                    }
                },
                active: 'image'
            }
        }
    </script>

    <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
    <script src="http://js.arcgis.com/3.11/"></script>
</head>

<body>

<div class="container voffset20px">
    <div class="row">
        <div class="col-xs-8">
            <button class="btn btn-success" id="btn-online-offline">Go Offline</button>
            <span class="span-pending">Pending Edits <span id="span-pending-edits" class="badge">0</span></span>
        </div>
        <div class="col-xs-4">
            <button id="btn-state" class="btn btn-success btn-large floatRight">
                <span id="state-span" class="glyphicon glyphicon-link"> Up</span>
            </button>
        </div>
    </div>
    <div class="row voffset20px">
        <div class="col-xs-12">
            <div id="mapDiv"></div>
        </div>
    </div>
</div>
<!-- Stub for modal popup -->
<div id="modal-popup"></div>

<script>

require([
        "esri/map","esri/tasks/query",
        "dojo/on","dojo/parser", "esri/renderers/SimpleRenderer",
        "esri/symbols/SimpleMarkerSymbol","esri/Color",
        "widgets/modal/popup","esri/layers/FeatureLayer",
        "//esri.github.com/bootstrap-map-js/src/js/bootstrapmap.js",
        "../dist/offline-edit-src.js",
        "dojo/domReady!"],
        function(Map,Query,on,parser,SimpleRenderer,SimpleMarkerSymbol,
             Color,ModalPopup,FeatureLayer,BootstrapMap) {

            var map, popup, editsStore;
            var defaultSymbol;
            var offlineFeaturesManager;
            var btnOnlineOffline, btnState, pendingEdits;
            var imgOfflineIndicator;
            var closeBtn,saveBtn,deleteBtn,stopMainID,stopID,stopRoutes,stopNames;

            initVars();
            initMap();

            function initMap(){

                map = BootstrapMap.create("mapDiv",{
                    basemap: "topo",
                    center: [-104.99,39.75], // longitude, latitude
                    zoom: 15
                });


                busStopFeatureLayer = new FeatureLayer("http://services.arcgis.com/IZtlGBUe4KTzLOl4/arcgis/rest/services/BPX_RTD_BusStops2/FeatureServer/0", {
                    mode: FeatureLayer.MODE_SNAPSHOT,
                    outFields: ["OBJECTID","BSID","ROUTES","STOPNAME"]
                });

                //Set the graphics to red boxes to make it easy to click on them
                //on a mobile device.
                busStopFeatureLayer.setRenderer(new SimpleRenderer(defaultSymbol));
                busStopFeatureLayer.on("update-end",function(evt){

                    // Now we can enable the button click listener
                    on(btnOnlineOffline,"click",goOnlineOffline);

                    initOfflineFeaturesMgr();
                    setFeatureLayerClickHandler();
                    setModalPopupClickListeners();
                });

                map.addLayer(busStopFeatureLayer);
            }


            function initVars(){

                editsStore = new O.esri.Edit.EditStore();
                popup = new ModalPopup({animation: true, animationDuration: 1},"modal-popup");
                popup.startup();

                parser.parse();

                // Check if browser state is online or offline
                Offline.check();
                Offline.on('up down', updateState );


                btnOnlineOffline = document.getElementById("btn-online-offline");
                imgOfflineIndicator = document.getElementById("state-span");
                btnState = document.getElementById("btn-state");
                pendingEdits = document.getElementById("span-pending-edits");

                // Modify symbol size based on screen size.
                // Bigger screens get smaller symbols. Smaller screens get larger symbols.
                var width = window.innerWidth
                        || document.documentElement.clientWidth
                        || document.body.clientWidth;

                var height = window.innerHeight
                        || document.documentElement.clientHeight
                        || document.body.clientHeight;

                if (height >= 768 || width >= 1024) {
                    defaultSymbol= new SimpleMarkerSymbol().setStyle(
                            SimpleMarkerSymbol.STYLE_DIAMOND).setColor(
                            new Color([255,0,0,0.5])).setSize(20);
                }
                else{
                    defaultSymbol= new SimpleMarkerSymbol().setStyle(
                            SimpleMarkerSymbol.STYLE_DIAMOND).setColor(
                            new Color([255,0,0,0.5])).setSize(35);
                }

                // Variables for modal popup
                closeBtn = document.getElementById("mod-popup-close-btn");
                saveBtn = document.getElementById("mod-popup-save-btn");
                deleteBtn = document.getElementById("mod-popup-delete-btn");
                stopMainID = document.getElementById("stop-main-id");
                stopID = document.getElementById("stop-id");
                stopRoutes = document.getElementById("stop-routes");
                stopNames = document.getElementById("stop-names");

            }

            /**
             * ************************************
             * OFFLINE FEATURE SERVICE HANDLER CODE
             * ************************************
             */

            function initOfflineFeaturesMgr(){
                offlineFeaturesManager = new O.esri.Edit.OfflineFeaturesManager();

                // IMPORTANT!!!
                // A proxy page may be required to upload attachments.
                // If you are using a CORS enabled server you may be able to set the proxyPath to null.
                // Refer to "Using the Proxy Page" for more information:
                //https://developers.arcgis.com/en/javascript/jshelp/ags_proxy.html
                offlineFeaturesManager.proxyPath = null;

                offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_ENQUEUED, updatePendingEditStatus);
                offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_SENT, updatePendingEditStatus);
                offlineFeaturesManager.on(offlineFeaturesManager.events.ALL_EDITS_SENT, updatePendingEditStatus);

                offlineFeaturesManager.extend(busStopFeatureLayer);
                console.log("offlineFeaturesManager initialized.");

                Offline.check();
                Offline.on('up down', updateState);
            }

            // Display modal popup when someone clicks on a feature
            // and load the fields with data from the feature service.
            function setFeatureLayerClickHandler(){
                busStopFeatureLayer.on("click", function(evt) {
                    currentFeature = evt.graphic

                    var query = new Query();
                    query.objectIds = [evt.graphic.attributes.OBJECTID];
                    busStopFeatureLayer.selectFeatures(query,FeatureLayer.SELECTION_NEW,
                            function(evt){
                                console.log("Success: " + JSON.stringify(evt[0].attributes));
                                showModalPopup(evt[0]);
                                var atts = evt[0].attributes
                                stopID.value = atts.OBJECTID;
                                stopMainID.value = atts.BSID;
                                stopNames.value = atts.STOPNAME;
                                stopRoutes.value = atts.ROUTES;
                            },function(err){
                                console.log("ERROR " + JSON.stringify(err));
                            });
                }.bind(this));
            }

            function updatePendingEditStatus(){
                if( editsStore.hasPendingEdits())
                {
                    var edits = editsStore.retrieveEditsQueue();
                    pendingEdits.innerHTML = edits.length;
                }
                else
                {
                    pendingEdits.innerHTML = 0;
                }
            }

            /**
             * ************************************
             * MODAL POPUP HANDLER CODE
             * ************************************
             */

            function setModalPopupClickListeners(){
                closeBtn.onclick = function(evt){
                    hideModalPopup();
                }

                saveBtn.onclick = function(evt){

                    popup.graphic.attributes.ROUTES = stopRoutes.value;
                    popup.graphic.attributes.STOPNAME = stopNames.value;

                    busStopFeatureLayer.applyEdits(null,[popup.graphic],null,function(result){
                                console.log("Successfully saved changes to: " +
                                    popup.graphic.attributes.STOPNAME);
                                hideModalPopup();
                            },
                            function(err){
                                alert("There was a problem while trying to save: " +
                                    popup.graphic.attributes.STOPNAME);
                            })
                }

                deleteBtn.onclick = function(evt){
                    busStopFeatureLayer.applyEdits(null,null,[popup.graphic],function(result){
                                console.log("Successfully deleted: " + popup.graphic.attributes.STOPNAME);
                                hideModalPopup();
                            },
                            function(err){
                                alert("There was a problem while trying to delete: " +
                                    popup.graphic.attributes.STOPNAME);
                            })
                }

            }

            function showModalPopup(graphic){
                popup.graphic = graphic; // assign graphic to modPopup as a property.
                popup.show();
            }

            function hideModalPopup(){
                popup.hide();
            }

            /**
             * ************************************
             * GO OFFLINE/ONLINE HANDLER CODE
             * ************************************
             */

            // Force feature service offline
            function goOnlineOffline(){

                if(btnOnlineOffline.innerHTML == "Go Offline"){
                    toggleStateUp(false);
                    console.log("Map is offline");
                }
                else{
                    toggleStateUp(true);
                    console.log("Map is online");
                }
            }

            // Set the UX to online or offline state
            function toggleStateUp(state){
                if(state){
                    btnOnlineOffline.innerHTML = "Go Offline";
                    offlineFeaturesManager.goOnline();
                    imgOfflineIndicator.className = "glyphicon glyphicon-link";
                    imgOfflineIndicator.innerHTML = " Up";
                    btnState.className = "btn btn-success btn-large floatRight";
                }
                else{
                    btnOnlineOffline.innerHTML = "Go Online";
                    offlineFeaturesManager.goOffline();
                    imgOfflineIndicator.className = "glyphicon glyphicon-thumbs-down";
                    imgOfflineIndicator.innerHTML = " Down";
                    btnState.className = "btn btn-danger btn-large floatRight";
                }
            }

            // Automatically set the feature service online or offline.
            function updateState(){
                if(Offline.state === 'up'){
                    toggleStateUp(true);
                }
                else{
                    toggleStateUp(false);
                }
            }
        }
);

</script>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</body>
</html>

                            
                        
NOTE: Replace paths with your references. Or build your app in the /demo directory
Pending Edits 0