jjackson-esristaff

Ember.js and the ArcGIS API for JavaScript

Blog Post created by jjackson-esristaff Employee on Mar 25, 2015

While I've used several ArcGIS APIs over the years, I'm relatively new to web development. I recently started developing a few apps that could be characterized as ambitious, so naturally I turned to Ember.js.

 

With a lot of experience developing for iOS and before that .NET, I've embraced MVC and MVVM. EmberJS is MVC done right. In fact, I find it very similar to the MVVM framework we developed for some of our Windows and Silverlight products.

 

EmberJS is fantastic. The more I work with it, the more I appreciate it. So naturally I tried to add a map to an app using our JavaScript API. It did not go well at first. Luckily, with some guidance from my esteemed colleague Frederic Aubry, I began to make sense of AMD (asynchronous module definition) and can now get the lego blocks to connect nicely.

 

I'm going to assume you already know Ember.js. There are great tutorials out there. My personal favorite is the video on the on the website Ember.js - Guides and Tutorials: Ember.js Guides . I learned a lot by pausing the video, typing in the code and experimenting. Another good tutorial, also on their site, is called Getting Started and walks you through building the TodoMVC demo application.

 

 

Getting Started

The easiest way to get started is to pull the starter kit for Ember emberjs/starter-kit · GitHub . Rename the folder to ember-arcgis-sample.

 

The first task is package up the logic of the app into modules. As it is now, the App class is simply a variable that is declared globally. To make use of ArcGIS functionality that comes prepackaged in modules, it's easier to use the same approach throughout. You can find a nice overview of AMD modules, module loading, packages, and more here -Introduction to AMD Modules - The Dojo Toolkit .

 

We will put all of our javascript modules into a single package and call it sample. Create a folder inside the js folder and name it sample. Then move app.js into the sample folder. We'll come back to the app.js file shortly.

 

Open index.html and add the following right after the <body> element:

 

<script>

     dojoConfig= {

        parseOnLoad: false,

        packages: [

            {"name":"sample", "location": location.pathname.replace(/\/[^/]+$/, "") + "/js/sample"}

        ]

     };

</script>

<script src="//js.arcgis.com/3.13/"></script>

 

This first script defines a variable dojoConfig that configures the dojo loader which is used by the ArcGIS API to load modules. It specifies a new package that will be available to the loader - our local package, sample. The second script loads the dojo module loader which is contained in the ArcGIS JavaScript API.

 

Next move the three scripts that load jQuery and Ember after the loader configuration script. Then add the following line:

 

<script src="js/libs/jquery-1.10.2.js"></script>

<script src="js/libs/ember-template-compiler-1.10.0.js"></script>

<script src="js/libs/ember-1.10.0.debug.js"></script>

 

<script src="run.js"></script>

 

The new script - run.js - will act as the initial entry point for our app. We'll look at that next. Note that the two handlebars scripts are now at the bottom of the html body. In a future post I will show how these can be removed from index.html altogether.

 

 

Create run.js

Create a file in the root folder of the project - ember-arcgis-sample - called run.js. We are going to move the Ember.js route definitions from app.js into run.js.

 

require([

   "sample/app"
], function (App) {

 

  // define the routes
  //
  App.IndexRoute = Ember.Route.extend({

        model: function() {

             return ['red', 'yellow', 'blue'];

       }

  });

  

  // start the router
  //
  App.Router.map(function() {

        // put your routes here
  });

 

  // we are now ready and can let the app
  // start running
  //
  App.advanceReadiness();

});

 

The thing to notice about this script is that it's wrapped by a call to require(). The require statement takes an array of modules as the first parameter. Note that the module we are requiring is the app module within the sample package. The second parameter to require is the function that will be called once every one of the requirements has been met - that is, once each of the modules has been loaded.

 

The function takes a series of parameters. Each parameter corresponds to the return value from the module that was passed in the first array parameter. So in our case, the app module will create the App instance and return it. That instance will be passed to us as the App parameter (you could name it anything you want).

 

The AMD loader will cache the return values of the modules it loads, so as we create additional modules that depend on the app module, we can be sure that the App instance will only be created once.

 

 

Modify app.js

All that is left of app.js is creating the App instance. But since we are turning app.js into a module, we need to modify it to use the define statement.

 

define(function () {

 

   var isDebug = true;

   var App = Ember.Application.create({

        LOG_TRANSITIONS: isDebug,

        LOG_TRANSITIONS_INTERNAL: isDebug,

        LOG_STACKTRACE_ON_DEPRECATION: isDebug
   });

   App.deferReadiness();

 

   return App;

});

 

The define statement defines a module. The module is contained in a function with an optional return value. In our case the module simply creates the Ember Application instance. We call deferReadiness() to delay the initialization process. This will become important later on when we are have more functionality contained in modules that will be loaded asynchronously.

 

Note that in the previous step when you created run.js you included a call to advanceReadiness() to continue the app initialization process that is delayed when the app is first created.

 

At this point you can run the app and you'll see the same results as before.

 

Add a Map

The last step is to add a map to your app. Modify run.js and add a new route to your app called map:

 

App.Router.map(function() {

   this.resource("map");

});

Then modify the IndexRoute. Remove the model and add a transition to the new map route:

 

App.IndexRoute = Ember.Route.extend({

        beforeModel: function (transition) {

             this.transitionTo("map");

       }

});

 

Add a new file to the sample folder called map-view.js.

 

define([

   "sample/app",

   "esri/arcgis/utils"
], function (App, arcgisUtils) {

 

  App.MapView = Ember.View.extend({

 

       onDidInsertElement: function() {

 

            var mapDiv = document.createElement("div");

            mapDiv.id = "map";

            this.element.appendChild(mapDiv);

 

            arcgisUtils.createMap("22d8f8438f294b3e8710bbade31b4ee6", "map").then(function(response) {

                 response.map.resize();

            });

 

       }.on("didInsertElement")

   });

});

 

The map-view module is dependent on the app module and the esri/arcgis/utils module. When it loads, it creates a new a new class - MapView - and adds it to the App instance. The meat of the MapView occurs when the corresponding DOM element is inserted. When that happens, a child div is added to the element and then we let the ArcGIS API create a Map corresponding to the specified webmap. You can see that webmap here http://nitro.maps.arcgis.com/home/webmap/viewer.html?webmap=22d8f8438f294b3e8710bbade31b4ee6

 

Next, you need to load the map-view module so that the MapView class can be defined. The simplest way to do that is to add sample/map-view as a requirement to the run.js script:

 

require([

     "sample/app",

     "sample/map-view"
], function (App) {

 

Finally, add a link to the arcgis stylesheet inside the <head> element of index.html.

 

<head>

   <meta charset="utf-8">

   <title>Ember ArcGIS Sample</title>

   <link rel="stylesheet" href="css/normalize.css">

   <link rel="stylesheet" href="css/style.css">

    <link rel="stylesheet" href="//js.arcgis.com/3.13/esri/css/esri.css">

</head>

 

That's it. Run your application and you should see a map!Screen Shot 2015-03-25 at 8.59.39 AM.png

You can find the source code of the completed app here jeffjax/ember-arcgis-sample · GitHub

Outcomes