2013-07-09 23 views
9

Sto tentando di utilizzare ArcGIS JavaScript API all'interno di un'applicazione Angolare. Come vedo, usa Dojo. Quindi, sto cercando di inizializzare ArcGIS dalla direttiva angolare in questo modo:AngularJS + ArcGIS

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); 
    } 

Sembra che in questo modo è corretto non al 100%, perché quando cerco di zoom scorrendo la rotellina del mouse, ottengo questo errore:

Uncaught TypeError: Cannot call method 'apply' of null 

La mia domanda è come inserire correttamente la funzionalità ArcGIS all'interno di un'app Angolare?

risposta

12

Penso che un approccio molto stile "AngularJS" a questo sarebbe qualcosa di simile al seguente. (armeggiate qui http://jsfiddle.net/technicolorenvy/2Ke62/4/)

Mi piace usare angular-ui-router, ma questo approccio funzionerebbe anche con Angular $routeProvider. La magia qui è nell'oggetto di risoluzione che opterà "aspetta" finché una promessa non viene risolta prima di continuare.

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; 
       } 
      } 
     }); 
    }); 

Come potete vedere sopra, abbiamo uno stato map che ha un puntello resolve. È quindi possibile creare un oggetto che rappresenti le dipendenze di ArcGIS/Dojo e trasmetterlo al numero wish.loadDependencies (vedere di seguito).

Usando q ritorneremo una promessa che viene risolto una volta che tutte le dipendenze vengono caricati tramite del dojo 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; 
      } 
     }; 
    }); 

dopo che, nel vostro MapCtrl, è possibile chiamare tutti i ArcGIS/Dojo FNS direttamente (come si normalmente), dai tasti utilizzati nell'oggetto deps che è stato creato durante la configurazione dell'app, poiché ora sono collegati all'oggetto restituito da wish.get().

Quello che segue è una versione modificata dell'esempio trovato qui (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); 
    }); 

violino di lavoro a dimostrazione del codice di cui sopra, trovato qui http://jsfiddle.net/technicolorenvy/2Ke62/4/

+0

mi piace la vostra soluzione, ma ho un piccolo problema. Sto usando angular translate e ci vuole una frazione di secondo per caricare init.js da "//js.arcgis.com/3.14", causando la traduzione angolare da mostrare come {{'qualcosa' | traduzione}} invece del testo renderizzato finché non viene caricato init.js per caricare 'require'. Sei a conoscenza di un modo per rimuovere la dipendenza richiesta all'interno del servizio desideri? – laitha0

+0

Lo gestisco nell'interfaccia utente tramite i flag preloader o "dati caricati". una spiegazione abbastanza decente del problema e la possibile soluzione qui http://www.code-hound.com/add-a-preloader-to-your-website-using-angularjs/ – technicolorenvy

+0

Grazie, ci proverò. – laitha0

7

Sembra che tu stia cercando di costruire la tua mappa all'interno di un elemento di direttiva. Questo è un uso legittimo, ma mi piacerebbe che tu usassi il caricatore AMD di Dojo per caricare i tuoi moduli e poi bootstrap la tua app Angolare dopo che tutta la bontà del Dojo è pronta.

Recentemente ho messo insieme una recensione su Angular/Esri dev e il codice sorgente per un progetto di esempio può essere trovato here.

Quello che effettivamente faccio è costruire la mappa da un controller, ma il processo dovrebbe essere simile a quello della direttiva.

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 }; 
}); 

Io uso un modulo di bootstrap per costruire tutti i miei pezzi angolari utilizzando i bit Esri/Dojo prima di bootstrap l'applicazione angolare.

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 }; 
}); 

La speranza aiuta un po '.