I am trying to use ArcGIS JavaScript API inside an Angular application. As I see, it uses Dojo. So, I am trying to initialize ArcGIS from Angular directive like this:
link: function (scope, element, attrs) {
dojo.require('esri.map');
var init = function () {
console.log('dojo is ready');
var map = new esri.Map("map-container", {
center: [-111.3797, 56.7266 ],
zoom: 16,
basemap: "streets"
});
map.enableScrollWheelZoom()
};
dojo.addOnLoad(init);
}
Looks like this way is not 100% correct because when I try to zoom by scrolling mouse wheel, I get this error:
Uncaught TypeError: Cannot call method 'apply' of null
My question, is how to properly to inject ArcGIS functionality inside an Angular app?
I am trying to use ArcGIS JavaScript API inside an Angular application. As I see, it uses Dojo. So, I am trying to initialize ArcGIS from Angular directive like this:
link: function (scope, element, attrs) {
dojo.require('esri.map');
var init = function () {
console.log('dojo is ready');
var map = new esri.Map("map-container", {
center: [-111.3797, 56.7266 ],
zoom: 16,
basemap: "streets"
});
map.enableScrollWheelZoom()
};
dojo.addOnLoad(init);
}
Looks like this way is not 100% correct because when I try to zoom by scrolling mouse wheel, I get this error:
Uncaught TypeError: Cannot call method 'apply' of null
My question, is how to properly to inject ArcGIS functionality inside an Angular app?
Share Improve this question edited Aug 20, 2015 at 13:17 Vikash Pandey 5,4436 gold badges42 silver badges42 bronze badges asked Jul 9, 2013 at 11:44 Sergei BasharovSergei Basharov 53.9k78 gold badges207 silver badges352 bronze badges2 Answers
Reset to default 13I 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.