Search code examples
javascriptdojoesriwmsarcgis-js-api

Dojo - Promise Can't Access Variables


I am working with dojo in the ESRI Web App Builder and have come across a situation where I need to run an AJAX call and still access a variable from the base class. Below is my code with comments in it to explain exactly where it is successful and exactly where it is failing:

define(['dojo/_base/declare', 'jimu/BaseWidget', 'dojo/request', "esri/layers/WMSLayer", "esri/config", "dojo/domReady!"], function (declare, BaseWidget, request, WMSLayer, esriConfig) {

    return declare([BaseWidget], {
        baseClass: 'jimu-widget-mywidget',
        // This function is called by a button press (Normally the WMSLayer variable would be set by user input)
        addWms: function () {
            var wmsLayer = new WMSLayer("http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer", {
                format: "png",
                visibleLayers: [2]
            });

            this.map.addLayer(wmsLayer); // this.map is inherited from BaseWidget as far as I can see. This adds a wms to my map without error

            request("request.html").then(function(data){
                var wmsLayer = new WMSLayer("http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer", {
                    format: "png",
                    visibleLayers: [2]
                });

                this.map.addLayer(wmsLayer); // This is now in another context....I get the error HERE. 
                // At this point map is not defined because this anonymous function is running
                // in a different context. ( at least I think that's what is happening )
            }, function(err){
                // Hopefully there are no typos in my example XD
            });
        }
    });
});

My question is --> How do I access the "map" variable from withing the callback function of "request"?

I want to be able to run this.map.addLayers from my call to the GetCapabilities of a WMS service. The request would normally call it and I get all the way to the end of my code until I can't access the "map" variable anymore as I know it.

Dojo type answer is preferred but plain old javaScript is also fine. Please avoid JQuery answers.

Resources are:
ESRI JavaScript library
Dojo
ESRI Web App Builder


Solution

  • The problem you're running into is the classic problem of execution context being lost when an asynchronous callback is invoked (and thus this no longer means what you want). There are generally two ways to resolve this.

    One way is to create a variable in the outer scope that references what this is, so that the inner function can access it:

    var self = this;
    request('request.html').then(function (data) {
        // ...
    
        self.map.addLayer(wmsLayer);
    });
    

    The other way is to use context binding, either via Function#bind (ES5) or dojo/_base/lang.hitch:

    // Function#bind:
    request('request.html').then(function (data) {
        // ...
    
        this.map.addLayer(wmsLayer);
    }.bind(this));
    
    // lang.hitch:
    request('request.html').then(lang.hitch(this, function (data) {
        // ...
    
        this.map.addLayer(wmsLayer);
    }));
    

    It's also common with the binding approach to break out the asynchronous handler function to another internal instance method:

    _addRequestedLayer: function () {
        // ...
    
        this.map.addLayer(wmsLayer);
    },
    
    addWms: function () {
        // ...
    
        // ES5:
        request('request.html').then(this._addRequestedLayer.bind(this));
        // ...or lang.hitch, with late binding:
        request('request.html').then(lang.hitch(this, '_addRequestedLayer'));
    

    There is also a tutorial on lang.hitch.