AngularJS + ArcGIS AngularJS + ArcGIS angularjs angularjs

AngularJS + ArcGIS


I think a very 'AngularJS' style approach to this would be something like the following. (fiddle here http://jsfiddle.net/technicolorenvy/2Ke62/4/)

I like using angular-ui-router, but this approach would also work w/ Angular's $routeProvider. The magic here is in the resolve object which will optionally 'wait' until a promise is resolved before continuing.

angular.module('webApp', ['ui.router'])    // module (app) config    .config(function ($stateProvider, $urlRouterProvider) {        $urlRouterProvider.otherwise('/map');        $stateProvider.state('map', {            url: '/map',            template: '<div id="map"></div>',            controller: 'MapCtrl',            resolve: {                promiseObj: function ($q, $rootScope, wish) {                    var deferred = $q.defer(),                        deps = {                            Map: 'esri/map',                            FeatureLayer: 'esri/layers/FeatureLayer',                            InfoTemplate: 'esri/InfoTemplate',                            SimpleFillSymbol: 'esri/symbols/SimpleFillSymbol',                            SimpleRenderer: 'esri/renderers/SimpleRenderer',                            SimpleMarkerSymbol: 'esri/symbols/SimpleMarkerSymbol',                            ScaleDependentRenderer: 'esri/renderers/ScaleDependentRenderer',                            Color: 'dojo/_base/Color'                        };                    wish.loadDependencies(deps, function () {                        deferred.resolve();                        if (!$rootScope.$$phase) {                            $rootScope.$apply();                        }                    });                    return deferred.promise;                }            }        });    });

As you can see above, we have a map state that has a resolve prop. You can then build an object that represents your ArcGIS/Dojo dependencies and pass that on to your wish.loadDependencies (see below).

Using q we will return a promise that gets resolved once all the dependencies are loaded via dojo's require

angular.module('webApp')     // service that deals w/ our dojo require    .service('wish', function () {        // it's not require... it's a wish?        var wish = {};        function _loadDependencies(deps, next) {            var reqArr = _.values(deps),                keysArr = _.keys(deps);            // use the dojo require (required by arcgis + dojo) && save refs            // to required obs            require(reqArr, function () {                var args = arguments;                _.each(keysArr, function (name, idx) {                    wish[name] = args[idx];                });                next();            });        }        return {            loadDependencies: function (deps, next) {                _loadDependencies(deps, next);            },            get: function () {                return wish;            }        };    });

after that, in your MapCtrl, you can call all the ArcGIS/Dojo fns directly (as you would normally), by the keys you used in the deps object that was constructed during app config, as they are now attached to the object returned by wish.get().

What follows is a modified version of the example found here (https://developers.arcgis.com/en/javascript/jssamples/renderer_proportional_scale_dependent.html)

angular.module('webApp')    // our map controller    .controller('MapCtrl', function ($rootScope, $scope, wish) {        var w = wish.get(),            greenFill = new w.Color([133, 197, 133, 0.75]),            greenOutline = new w.Color([133, 197, 133, 0.25]),            layer,            markerSym,            renderer1,            renderer2,            CROPS_URL = 'http://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/USA_County_Crops_2007/FeatureServer/0';        $scope.map = new w.Map('map', {            center: [-98.579, 39.828],            zoom: 4,            basemap: 'gray'        });        layer = new w.FeatureLayer(CROPS_URL, {            outFields: ['STATE', 'COUNTY', 'M086_07', 'AREA'],            infoTemplate: new w.InfoTemplate('${COUNTY}, ${STATE}', '<div style="font: 18px Segoe UI">The percentage of the area of the county that represents farmland is <b>${M086_07}%</b>.</div>')        });        layer.setDefinitionExpression('AREA>0.01 and M086_07>0');        markerSym = new w.SimpleMarkerSymbol();        markerSym.setColor(greenFill);        markerSym.setOutline(markerSym.outline.setColor(greenOutline));        renderer1 = new w.SimpleRenderer(markerSym);        renderer1.setProportionalSymbolInfo({            field: 'M086_07',            minSize: 1,            maxSize: 10,            minDataValue: 0,            maxDataValue: 100        });        //for the second renderer increase the dot sizes and set a backgroundFillSymbol        renderer2 = new w.SimpleRenderer(markerSym);        renderer2.setProportionalSymbolInfo({            field: 'M086_07',            minSize: 5,            maxSize: 15,            minDataValue: 0,            maxDataValue: 100        });        layer.setRenderer(new w.ScaleDependentRenderer({            rendererInfos: [{                'renderer': renderer1,                    'minScale': 50000000,                    'maxScale': 10000000            }, {                'renderer': renderer2,                    'minScale': 0,                    'maxScale': 5000000            }]        }));        $scope.map.addLayer(layer);    });

working fiddle demonstrating the above code, found here http://jsfiddle.net/technicolorenvy/2Ke62/4/


It looks like you are trying to build your map inside a directive element. That's a legitimate use, but I would make sure that you utilize the AMD loader of Dojo to load your modules and then bootstrap your Angular app after all the Dojo goodness is ready.

I recently put together a write-up on Angular/Esri dev, and the source code for a sample project can be found here.

What I actually do is build the map from a Controller, but the process should be similar to building it in the directive.

define([    'angular',    'esri/map'], function(angular, Map) {    function mapConfigs() {        return {            basemap: 'streets',            center: [-118.1704035141802,34.03597014510993],            zoom: 15        };    }    function mapGen(elem) {        return new Map(elem, mapConfigs());    }    function AppController($scope) {        $scope.map = mapGen('map');    }    function init(App) {        App.controller('AppCtrl', ['$scope', AppController]);        return AppController;    }    return { start: init };});

I use a bootstrap module to build all my Angular bits using the Esri/Dojo bits before bootstrapping the Angular application.

define([    'angular',    'controllers/AppController',    'widgets/search/SearchBootstrap'], function(angular, AppController, SearchBootstrap) {    function init() {        var App = angular.module('app', ['ui.bootstrap']);        AppController.start(App);        SearchBootstrap.start(App);        // need to bootstrap angular since we wait for dojo/DOM to load        angular.bootstrap(document.body, ['app']);        return App;    }    return { start: init };});

Hope that helps a bit.