Hi,
I have quite a few calls like this in my JavaScript:
dojo.connect(map, "onLoad", {map: map, do:buildScalebar}, "do");
and I would like to simplify to something like this:
var scalebar = buildScalebar({ map: map });
// the retuned value should be the scale bar widget so I could do:
scalebar.hide();
scalebar.show(); // and so on
What is the best way to do it?
Here is the buildScalebar function from the second case, which however returns null.
function buildScalebar(opts){
/* Build the scalebar widget with options:
* .map -- map for which to load the scalebar
* .unit -- scalebarUnit, dual|metric|english, default is "dual"
* .anchor -- top-right|bottom-right|top-center|bottom-center|bottom-left|top-left, default value is "bottom-left"
*
* Returns the widget. (BUT IT DOESN'T!!!)
*/
var o = this;
if ('map' in opts) { o = opts; }
var map = opts.map;
var anchor = 'anchor' in opts ? opts.anchor : "bottom-left";
var unit = 'unit' in opts ? opts.unit : "dual";
var scalebar = null;
dojo.connect(map, "onLoad", {
"map": map,
"attachTo": anchor,
"scalebarUnit": unit,
"do": function () {
var options = {"map": this.map, "attachTo": this.attachTo, "scalebarUnit": this.scalebarUnit }
scalebar = new esri.dijit.Scalebar( options );
}
}, "do");
return scalebar;
}
I would like to use this pattern for other widgets and functionality, not just the scale bar widget.
Is it a good idea? Do I need some kind of Deferreds here? Can you help me with an example?
Cheers,
Filip.
Solved! Go to Solution.
Using a Promise such as dojo/Deferred will probably be the most reliable to accomplish close to what you are asking. The main sticking point is that you are delegating the responsibility of waiting for the map to load to the createScalebar methods, which is an asynchronous process. The Scalebar widget will internally already wait for the map to load, so there is really isn't a need for you to do it also in this case.
What the Scalebar does not do at the moment unfortunately is emit a load event. Someone mentioned in a thread somewhere that in a future release, all widgets will emit load events. If you leave out the waiting for the map to load, your original code works fine.
With widgets, when you writing your own and they are dependent on the map, then you should be concerned about waiting for the map to load, similar to how this esri dijit for the geocoder works on github.
If you really need to wait for the load of the map in that create method, I think a Promise is a good way to do it.
function buildScalebar(opts) { var def = new Deferred(); var _map = opts.map; var anchor = ('anchor' in opts) ? opts.anchor : "bottom-left"; var unit = ('unit' in opts) ? opts.unit : "dual"; _map.on('load', function() { var sb = new Scalebar({ map: _map, attachTo: anchor, scalebarUnit: unit }); def.resolve(sb) }, def.reject); return def.promise; } buildScalebar({ map: map }).then(function(scalebar) { scalebar.hide(); scalebar.show(); });
Hope that helps a bit.
Hi Rene,
I marked your answer as correct because I also found that the use of deferreds is the only way to ensure that a block of code is executed only after some other block of code has finished.
As you indicated, the def.resolve(sb) on your line 12 should be more like sb.on('load', function(){ def.resolve(sb); });, if only the widget emitted a load event when ready.
When I originally asked the question, I was hoping for a more simple answer. I find this way of coding rather verbose and confusing, especially if you want to wait for multiple asynchronous calls and make them all play together. Quite a few examples show use of deferrds in basic scenarios but I cannot find a nice guide to structuring JavaScript code in larger applications. If you know about a good resource for this, please share.
Thank you all for contributions,
Filip