jarcher-esristaff

Old Dog, New Tricks - Moving Your Code and Your Brain From Legacy to AMD

Blog Post created by jarcher-esristaff Employee on Sep 2, 2014

Disclaimer:  I want to set the tone here about the intended audience for this post.  If you spend all day every day building, testing, and deploying web apps built with the Esri ArcGIS API for Javascript, this is not for you.  If however, you are one of the legions of GIS professionals forced to wear a closet full of hats, and one of those is “Javascript developer”…  You might get something out of this.  I’d say the best way to describe my target audience would be as a Geospatial professional who is either new to the Esri Javascript API, or – even better – has done some work with it in the past, and now wants or needs to know what all the fuss is about with AMD.  I’m sure I’ll get flamed for falling on either side of that target, but such is the web…

 

In my years of work at Esri, I’ve had the pleasure of writing a bit of code.  I wouldn’t call myself a developer in the true sense of the word, but I’ve played the part on several stretches in my career.  I streamlined many a workflow with AML and Avenue way back when.  I’ve done mountains of string manipulation with Javascript for ArcIMS applications. I’ve also had the “experience” of developing with the .NET ADF in the early days of ArcGIS Server.  Most recently, I’ve circled back around to web apps and the ArcGIS API for JavaScript.  I was able to work on the first versions of the Public Information Map, among other projects. As a result, I got to be quite versed in the Esri JavaScript API, and good enough to be dangerous with HTML and CSS.

 

My career has been progressing however, and for the past couple of years, my web and mobile development opportunities have been spotty at best.  Most of my efforts have been put toward other activities like project management.

 

I was excited recently to be tasked with creating a series of samples that were intended to be prototypes for how web and mobile applications might use time-based datasets to make themselves more interesting to some real-world workflows.  This work revolved around a set of use cases generated by the Esri Disaster Response Program (DRP).  I won’t get into too much detail, as the results of this work won’t be out for some time, but I will say “stay tuned!”  There should be some interesting stuff coming soon revolving around how time-based data can be used to make more informed decisions.

 

Of course, this was a great opportunity, and I jumped at it.  I had been thinking a lot about the good old days of writing code in much the same way I used to daydream about getting back in to the hay fields as Summer used to approach while I was in college.  A good, honest day’s coding makes a person feel pretty good.

 

There was a problem though. While I had been away, the Javascript API and the Dojo toolkit on which it is based both underwent a profound change. Gone were the simple days of single javascript libraries and bulk “everybody in the pool!!” page loading.  A new sheriff was in town, and it’s name was AMD.

 

Now, like any good pseudo-developer, I’ve kept my finger on the pulse of the AMD revolution while going about my new career responsibilities.  I got this, I said.  No big deal, right?  I just have to reference the parts of the Javascript API that I need, and that’s all that gets loaded.  “Cake!” I said.

 

Not so fast, my friend. For my new prototype work, I would be using Matt Driscoll’s excellent Application Boilerplate for the starting point. There are a lot of reason’s for this, most of which are documented in the github repo.  Now, Matt is a very bright developer, and has had the luxury of coding full time for the past several years.  He was literally right in the middle of this whole AMD movement, and this template is a reflection of that.  In fact, it is a great example of AMD in action and how it affects developers who use the Javascript API.

 

 

Why AMD?

 

What does AMD bring to the table?  This could be (and has been) a whole series of blog posts by itself.  I’ll do the cop-out thing and reference a few key resources that do much better job than I can of explaining why both the Dojo community (Sitepen) and Esri thought AMD was good enough to change the architecture of the Dojo framework and the Esri Javascript API to support it.

 

https://github.com/amdjs/amdjs-api/wiki/AMD

 

https://developers.arcgis.com/javascript/jshelp/inside_dojo_amd.html

 

http://dojotoolkit.org/documentation/tutorials/1.9/modules/

 

I will say quickly that what you generally do is define “modules” as points of reference in your code that contain all kinds of good stuff (functions, variables, objects) you want to work within that modular context.  Once the module is defined, you can conveniently reference all the stuff in it as long as your scope is correct or corrected – this.map, this.myVar, this.handleQueryResults(results).  Get it? Advanced “Module” Definition?  Wink, wink.

 

I could define a module that handles all the framework and page setup when a page first loads, and another to handle all of the user interaction and presentation stuff with the map.  We will see an example of this in the Application Boilerplate a bit farther down.

 

If this is furrowing your brow, you really should take some time to read through the above links.  It will help make the Query Task study make a bit more sense.  Don’t worry, it can take a while to get your head around this stuff.  My brow was certainly furrowed more than most!  Take your time and keep reviewing until it starts to make sense.

 

 

Legacy Study – Query Task

 

This is the comfort zone for a lot of us - a single browser window in which our code will execute. If we’re feeling ambitious, we might use the dojo framework to declare and use a few classes.  This allows us to only invoke the parts of our code that we need at a given time, and opens the door for code re-use.  Mainly though, we just want to build a map and some basic functionality we need to implement for it.  Take the following example:

 

Legacy_QueryTask

 

At first glance, this seems like such a simple and elegant way to write code.  All I need is one Javascript file, and I can just script up whatever I need and it will just work.  Hard to argue this point if your application requirements are as simple as this sample.

 

But what happens when you start bringing in more functionality?  Let’s say you embed this simple map into a larger page with several library dependencies and possibly another Javascript API?  What if you need to include editing in the map or enable access to Geoprocessing and Geo-enrichment services and send the output to other, non-map centric parts of the site?  What about… mobile?

 

Doesn’t look so attractive now, does it?  It’s only a matter of time before you run into problems that you have to work around with cheesy stuff like timeouts (so I hear, I’ve never actually done it, I swear! ).

 

 

AMD Study – Query Task

 

Now, let’s try this same thing again with Matt’s Application Boilerplate Template.  A detailed tour of how this template works is beyond the scope of this post, but should make sense if you read through all the links provided above and have a journeyman’s grasp of AMD.  Take some time to download the code for the base template and the Query Task AMD sample from Github and look at how the files and code are arranged.

 

Application Boilerplate

 

AMD_QueryTask

 

When you look through the template or the Query Task AMD sample that uses it, you will see how Matt uses AMD to solve a lot of the problems associated with the legacy way of building applications.  Things like page load life cycle and class creation are handled clearly and efficiently by compartmentalizing code into “modules” (there’s that word again!) and defining the order and context in which they execute.  I will say however, to pay particular attention to 3 things, because they are sure to trip up those new to using the AMD approach with the Esri API for Javascript:

 

  1. Scope matters! That wasn’t a big deal when all the code was just dumped into the window, but it is a big deal now.  You can use “this” and “lang.hitch” as tools to make scope management easier.
  2. Storyboard how your page loads and executes and recognize where modules make sense. You can reference index.html and the order the javascript code is arranged as the rough storyboard for this template.
  3. When defining a module, you have to keep all your dependencies and their references in the same order.

 

Let me show you what I mean about 3).  The query task will be using several dependencies in the “main” module definition block:

 

define(["dojo/ready", "dojo/_base/declare", "dojo/_base/lang",

"dojo/query", "esri/toolbars/draw",

      "dijit/Dialog", "esri/arcgis/utils", "dojo/dom",

"dojo/dom-class", "dojo/on", "esri/config",

      "esri/symbols/SimpleMarkerSymbol",

"esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleFillSymbol", "esri/tasks/QueryTask",

      "esri/tasks/query", "esri/tasks/StatisticDefinition",

      "esri/symbols/PictureFillSymbol",

"esri/symbols/CartographicLineSymbol", "esri/graphic", "esri/TimeExtent", "esri/Color"],

function (ready, declare, lang, query, draw, dialog, arcgisUtils,

dom, domClass, on, esriConfig, SimpleMarkerSymbol,

SimpleLineSymbol, SimpleFillSymbol, QueryTask, Query,

StatisticDefinition, PictureFillSymbol,

CartographicLineSymbol, Graphic, TimeExtent, Color) {

...

 

 

Note how all of the function variables are in exactly the same order as they were declared.  This is a very common “gotcha” when new developers start out in AMD.  Another important point is that the variables we list here are the case-sensitive way that we can use these in this module.  For example, if I want to implement the draw toolbar, I would do so by “newing” the draw object with the case sensitive reference defined in the module definition function.

 

var myToolBar = new draw(this.map);

 

Note how I referenced “map” here.  In the legacy days, the map was a global entity that could be referenced anywhere in my code.  It just lived in the window and I could get to it very easily with a global reference. However, AMD requires that I define the scope in which the map object exists, and reference it as a part of that scope.  In the sample, I’ve set the scope of the map to be my module.  This may seem a bit tedious, but understand that it buys us a lot of efficiency at runtime.  We are able to compartmentalize code and focus it to a specific context.

 

Another example of scope would be those global variables I set up and the beginning of the code block. I can also define functions in this scope and reference any of these variables within those functions by simply putting “this.” in front of them.  One may be a constructor for when I invoke this module.  Others might handle things like callbacks.

 

Let’s look at the callback thing a little more closely.  This is a great example of how we need to manage scope inside a module for a very common Javascript API operation.  In fact, it’s the core study for this post!!

 

For my query task, I will need to make use of “this” and the dojo lang.hitch mechanism to set up my callbacks properly.  Note that I have to also handle the query task output results in my callback reference. This is one of those things that has to be just so within AMD, or else the function might get called and executed right away – before my results come back via the promise – or the results might not be passed into it after they come back from the server.

 

     precipQueryTask.execute(precipQuery, lang.hitch(this,function(results){

          this.handlePrecipQuery(results);

     }));

 

Using ‘lang.hitch’ in this way makes sure that I’m passing the current code execution context (“this”) to my handler function.  That way, when I assign my handler function with this.handlePrecipQuery(results), I know that not only will the handler function be called correctly, and that my results will be wrapped in a promise that will fire after my query executes, but also that the results of the query will be passed into the callback function.

 

Hopefully this simple query example shows how to implement a very common workflow within AMD and specifically the Application Boilerplate Template.  I’ll carry on now with a couple other useful observations.

 

Bonus:

 

Getting back to a point I made above: A great example of how Matt uses the AMD coding model is in the last <script> block in index.html.  Take a look:

 

require(["application/templateOptions","application/template","application/main"],      function(templateConfig, Template, Mail){

          // create the template. This will take care of all the

          // logic required for template applications

          var myTemplate = new Template(templateConfig);

          // create my main application. Start placing your logic in the

          // main.js file.

    var myApp = new Main();

    // start template

          myTemplate.startup().then(function(config) {

    // The config object contains the following properties: helper

    //services, (optionally)

    // i18n, appid, webmap and any custom values defined by the //application.

    // In this example we have one called theme.

          myApp.startup(config);

     }, function(error) {

          // something went wrong. Let's report it

          myApp.reportError(error);

     });

});

 

This compartmentalization of the code allow him to have control over how the code executes on page load.  First, he invokes the template with the templateConfig, which will execute the code in template.js.  Next he invokes Main as “myApp” and passes in the config file, which will execute all the code in main.js.  In between, he has used the dojo framework’s promises (then) to make sure that the Main object is not started up until the Template has completely finished executing. How elegant!!  Built into the Application Boilerplate is the ability to set up the browser and the environment where our code will execute, and then a nice place to put all of our functional code for the JSAPI.

 

Conclusion:

 

This is just a simple example of how the AMD framework changes the way you have to think about the organization of the code in your application.  If there are only 2 things you get out of this, it should be that there are 2 big gotchas to look out for as you move from the legacy model to the new AMD model:

 

  • Make sure you keep the declarations and references in order in your declare statement
  • Scope matters!! Make sure you’re familiar with dojo tools like lang.hitch and REALLY understand what “this” means for your code!! Don’t fake it!!

 

Once you have these 2 things mastered, you are ready to start doing some AMD-friendly javascript development.  That, or you can do like I did in this sample and build on top of all the great work Matt has put into the Application Boilerplate Template.  The kicker there is that you could then easily turn your work into a template in your ArcGIS Online Organization.

 

As for the time-based data work with the Esri DRP, stay tuned!  There will be more information coming out about this project as it progresses.

 

Jeff Archer

Senior Technical Engineer

Technical Marketing Group

Esri, Inc.

Outcomes