Using AngularJS with the ESRI JavaScript API

I’ve been doing quite a bit of learning about AngularJS since it seems to be the current hot topic in JavaScript. Since I had to rework a map for the city (I’m currently employed with the City of Scottsdale), I decided to see if AngularJS could be used in partnership with the ESRI JavaScript API to make creating, structuring, and displaying data that much easier.

Being that the ESRI API is based on Dojo, we are duplicating some of that functionality by bringing Angular into the picture. I’ve built a full app using only Dojo with the ERSI API and I’ve had issues with doing it with pure Dojo. Those being:

  1. Dojo doesn’t do two-way binding. When developing a data-heavy map app, that starts to become quite useful.
  2. Dojo’s widgets don’t seem to integrate well with other widgets. They do have an event structure to communicate between modules, but that’s primarily the only way it can be done. This is somewhat of a by-product of Dojo being a toolkit, rather than a framework. An application structure is not implied by Dojo.
  3. Dojo’s unit testing support is something that I cannot understand. They have their own harness that is used for testing Dojo code but I cannot get it to work. That may just be how I was trying to use it, or how I structured the widgets in the app, but I was having a difficult time.

For those three reasons, Angular seemed like a good idea as it resolves those issues. Angular also does all three of them quite well. Angular is a framework built with MVC in mind, this implied structure makes communication and creating modules much easier. It was also built with unit testing in mind. Apart from the necessary map widgets, I’ve tried to avoid using Dojo to increase testability. Obviously, mocking the Dojo pieces could prove difficult but I’ll go more in depth about that in another post.

I figured I would share a simple example of creating a map with layers using Angular. This may not be the most efficient or best way but it works for what I’m doing for the city maps. Let me know if there’s any ways that it can be done better as I’m very much still learning Angular.

This post does assume a working knowledge of Angular. If you know nothing about it, I suggest going to their site, do some examples, and come back as the code will make much more sense. It also assumes a little knowledge of the ESRI API. I’ve got a couple posts on creating a map, drawing a point, and displaying data. That is a good starting point and is more than enough to get you going with this post.

Let’s get to the code.

Getting Going

To start, we need to bring in the ESRI API and Angular, obviously:

We also need to setup Dojo’s AMD, so that we can seperate our modules into seperate files, to be able to pull local modules. I’m just going to do this inline instead of creating a new file since we only need to setup our local package.

This line needs to be added before you bring the ESRI API. Update the location with where you’re local script files will be.

Create the Map Directive

We need to create a directive for the map container. This directive will instantiate the map and attach some events to the map that various scopes can tap into.

Let’s create the map module:

Next, we want to create the directive definition:

If you are not familiar with some of the options, I suggest looking through Angular’s guide on directives. One thing that may look a little different is attaching a different controller and passing the element to that controller. I got that structure from ui.bootstrap, which has a collection of directives based on Twitter Bootstrap. This article breaks down that method better than I could. They talk about controllers being easier to unit test than putting everything inside the directive. We can test the controller in isolation from the directive. Some say the controller should not have access to the DOM, but this way we can mock the DOM element when testing the controller as well.

Now for our map controller:

The createDiv function basically makes the container for the map to attach to, this way if there’s any directives inside the map directive, the ESRI map won’t remove whats in there. We also give support for changing some of the map options via HTML attributes (there’s an example of this at the end of the post). The only attribute that is made required is the id attribute as that’s the way the map attaches itself to the div. createMap is pretty self-explanatory; create the map and attach the events. We also check to see if there’s any layers to add that were created before the map was created.

We added an addLayer method to the scope so that layers inside the map can get added. We need to check if the map exists so we defer the adding of the layer. This is just to make sure layers won’t attempt to get added before the map is fully loaded. This will be used in the next section.

The HTML:

Adding Layers to the Map

What’s a map if you can’t add layers to display data on it? We will follow the same directive structure as we did with the map.

I’m going to only show creating feature layers in this post, which you can read more about in a previous post I made about displaying custom data using feature layers. I’ll be using a sample layer that ESRI uses through some of their samples. Other layers like the Tiled, or Map Service, will somewhat follow the same structure. I can go more in depth about those if needed in another post.

Module definition:

Now for the directive:

This is similar to how we setup the map directive with some small differences, one being the require attribute. This forces the directive to be inside our esriMap directive. Angular gives us a bunch of other benefits like being able to access esriMap‘s scope meaning we can get to the map if we need to, as well as other methods we allow access to, like the addLayers¬†method. We also don’t need to pass in the element to the controller since we won’t be modifying the element itself at all.

Let’s create the controller:

The controller is simple. We just check some required attributes, instantiate the Feature Layer, then add it to the map. Here’s an example of the HTML with map and layer together:

That’s it. Now you have a simple structure to create a map and add feature layers to it using only HTML. With all that being said, you should get something like the following:

Angular ESRI Example

Angular ESRI Example

Wrap Up

This was meant to just be a ‘getting started’ post. I have plans to talk about clicking on features to display their attributes and creating custom map specific widgets like a toolbar or street/satellite selector. I also want to show how to write unit tests for the two directives created above. Also, if you’re an Angular master and can let me know how my code can be improved, let me know in the comments, or let me know if there’s any issue you have using Angular with the ESRI API.


Update 8/29/2014: Typo in the one of the code samples that would make it not run. Thanks for pointing that out Jeffry Houser!

  • mikefoxtrottango

    Your article is very relevant to what I need to learn and its a great starting place. Is it possible to provide the source code for this example? I’m trying to figure out how many files you split this into. Just two? One HTML file and one JS file? I don’t see a reference in your HTML file to a JS file that contains the module, directive, and controller definitions. Thanks in advance.

    • Jeffry Houser

      Mike; I could jnever get Justin’s code working as he describes [although it came close].

      My own writeup is here:: http://www.jeffryhouser.com/index.cfm/2014/11/3/Turning-the-ESRI-Map-Component-into-an-Angular-Directive which uses Justin’s post as a jumping off point.

      And the code in Github: https://github.com/Reboog711/AngularEsriSample

      • mikefoxtrottango

        Thanks, Jeff. I pulled the code from Github. Very helpful.

    • justinchmura

      I’m also in the middle of writing up an update to this post. I see that there’s other people looking to tie these two frameworks together. I’ll provide better examples and runnable code in the next post. I’ve learned quite a bit in the mean time.

      Sorry for the errors in this post. Also, do check out Jeffry’s post, that will help you too.

      • mikefoxtrottango

        Much appreciated, Justin. Between you and Jeffry, I’m off and running.

  • AhammadaliPK

    Hi.

    Jeffry..I used this code as my first example, But it sounds an error..could you please give me the solution..

    I will paste the error,

    Unhandled exception at line 34, column 241 in https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.10/angular.min.js

    0x800a139e – JavaScript runtime error: [$injector:modulerr] http://errors.angularjs.org/1.3.0-beta.10/$injector/modulerr?

    Thank you for your valuable comment in advance..

  • Eric Wood

    Hey Justin, I know this is a really old post but I thought you might find it funny that your article came up for me a couple of years after we worked on this project. I am at a different organization now and working on an ESRI app inside Angular but am using v4.1 of ESRI’s api. I found that ESRI has some directives on GitHub than seem pretty solid. They work out-of-the-box for simple maps and provide a good set of patterns for more complicated stuff. They have different branches for 3.x and 4.x.

    https://github.com/Esri/angular-esri-map

    • That is funny. Yea it seems this article is somewhat popular which is surprising. What version of Angular are you using? I haven’t played with the latest API at all but from what I saw it’s a lot easier to work with.