JavaScript API

Migrating to AMD-style JavaScript

2015 WLIA Chris Cantey / @chriscantey

About Me

How do I use the ArcGIS JS API?

Single Page Applications

Customize (Hack) ArcGIS Online

Multiple Page Applications

Single Page Application

Customize ArcGIS Online

Multiple Page Application

Some (brief) history...

<script> tags

<script src="js/libs/jquery.js"></script>
<script src="js/libs/underscore.js"></script>
<script src="js/libs/backbone.js"></script>
<script src="js/libs/app.js"></script>

Pollutes the global scope

Difficult to manage dependencies

Dojo Toolkit

Serves as the foundation for the ArcGIS JS API

Module loading for managing code in large applications


Legacy API

// index.html

// index.html
geometryservice = new esri.tasks.GeometryService("geometryServiceURL");


// app.js
console.log(geometryservice.url) //


(Asynchronous Module Definition)

An offshoot of CommonJS that emphasizes the browser

Used by script loaders like RequireJS and Dojo.js.

ArcGIS JS API Version 3.4

AMD Spec

AMD in a Nutshell

define(id?, dependencies?, factory);

define(id?, dependencies?, factory);

Give our module a unique id (which is really just a path).

define('path/to/Module', function() {

RequireJS discourages the use of module ids.

define(id?,  dependencies? , factory);

List any dependencies in an Array and DoJo will automatically inject them into your module.

// dojo/dom assigned to dom
// esri/Color assigned to Color
// esri/map assigned to map

define(['dojo/dom', 'esri/Color','esri/map'], function(dom, Color, map) {

What's going on behind the scenes?

When DoJo sees this:

// app.js
define(['app','jquery', 'd3'], function(app, $, d3) {

It turns it into this:

  <script src="./js/app.js"></script>
  <script src="./js/jquery.js"></script>
  <script src="./js/d3.js"></script>

define(id?, dependencies?, factory);

Called once per module. If the factory function returns anything then that object should be assigned as the exported value for the module.

define(['jquery', 'highstock'], function($) {
  return {
    // whatever is returned here is
    // the exported value of the module

Things you can do with the factory function...

Return an Object

// person.js
define(function() {
  return {
    name: 'Chris',
    sayHello: function() {
      alert('Hi, my name is ' +;

// app.js
define(['person'], function(person) {
  person.sayHello(); // alerts 'Hi, my name is Chris'

Return a Function

// sum.js
define(function() {
  return function(a, b) {
    alert(a + b);

// calculator.js
define(['sum'], function(sum) {
  sum(2, 2); // alerts 4

Return constructors!

// person.js
define(function() {

  function Person(name) { = name;
    this.sayHello = function() {
      console.log('hello, my name is ' +;

  return Person;


// app.js
define(['person'], function(Person) {
  var chris = new Person('Chris');
  chris.sayHello(); // 'hello, my name is Chris'

Create private variables and functions

// basket.js
define(function() {
  // Private
  var counter = 0;

  function getCounter() {
    return counter;

  function incrementCounter() {

  // Public
  return {
    count: function() {
      return getCounter(); // return value of private var
    addToCart: function() {
      incrementCounter(); // call private function

One more thing!

require(dependencies?, callback);

// index.html
require(['jquery'], function($) {
  // kickoff your application!
  $(document).ready(function() { ... });


Dependency order matters!

define(['jquery', 'someJqueryPlugin'], function($) {

  /* $ is mapped to the first dependency, which is jquery */


Totally cool.

define(['someJqueryPlugin', 'jquery'], function($) {

  // $ is mapped to the first dependency, which returns undefined
  // because it's a plugin!!!


No bueno, dude!!!

Don't mix async and synchronous code

// index.html

require(['jquery', 'widget', 'highcharts'], function($, widget) {

  /* do something interesting with jquery */


<!-- Breaks because Highcharts hasn't loaded yet -->
<script src="someHighchartsPlugin.js"></script>

<!-- Breaks because widget hasn't loaded yet and isn't
        available in this scope -->
<script type="text/javascript">

Use shims or define your own AMD modules instead.

Syntactics and Semantics

A lot has changed!

Backwards Compatibility

Basic Patterns changed

// legacy
dojo.connect(map, 'onLoad', function ());

// AMD
require(["esri/map", "dojo/on"], function(Map, on) {
  map = new Map("map", {
                extent: initExtent  
  on(map, "load", function(){


require(["dojo/on"], function(on){
  on(target, "event", function(e){
    // handle the event

Managing Asynchronous Threads

// indentification.js
        "identifyParameters"], function(IdentifyTask, identifyParameters) {

  identify: function (evt) {
	identifyTask = new IdentifyTask(config.mapServices.dynamic + "?token=" + token);
	identifyParameters = new IdentifyParameters();
	/* take in all parameters */
	identifyTask.execute(identifyParameters).then(lang.hitch(this, function (idResults) { 
		this._utility(idResults, evt);                 
   }, /*carry on with other methods*/





Tell dojo where to find your modules.


var path_location = location.pathname.replace(/\/[^/]+$/, '');
var dojoConfig = {
	paths: {
		config: path_location,
		app:  path_location + '/js'

require(['app/main', 'app/navigate', 'app/identifyParcel', 'esri/map', 'esri/draw/]);

Single Page Application

See ArcGIS JS API for examples

Customizing AGOL Apps

Good for DOM Manipulation

A bit hard to do anything else

REST vs WebmapID

Multiple Page Applications

Define your own modules!

Manage your scope!

Start with API examples and move forward


- @chriscantey

- Special thanks to John Gravois (Esri)