2013-07-09 7 views
9

Ich versuche ArcGIS JavaScript API innerhalb einer Angular-Anwendung zu verwenden. Wie ich sehe, benutzt es Dojo. Also, ich versuche ArcGIS von Angular Richtlinie wie folgt zu initialisieren: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); 
    } 

wie auf diese Weise Looks ist nicht 100% richtig, denn wenn ich durch Scrollen Mausrad zu vergrößern versuchen, ich diesen Fehler:

Uncaught TypeError: Cannot call method 'apply' of null 

Meine Frage ist, wie ArcGIS-Funktionalität in einer Angular-App richtig zu injizieren?

Antwort

12

Ich denke, ein sehr "AngularJS" Stil Ansatz dazu wäre etwas wie das Folgende. (Geige hier http://jsfiddle.net/technicolorenvy/2Ke62/4/)

Ich mag angular-ui-router verwenden, aber dieser Ansatz würde auch w/Angulars $routeProvider funktionieren. Die Magie liegt hier im Auflösungsobjekt, das optional "warten" wird, bis eine Verheißung aufgelöst wird, bevor es fortgesetzt wird.

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

Wie Sie oben sehen können, haben wir einen map Zustand, der eine resolve Stütze hat. Sie können dann ein Objekt erstellen, das Ihre ArcGIS/Dojo-Abhängigkeiten darstellt, und dieses an Ihre wish.loadDependencies weiterleiten (siehe unten).

Mit q wir ein Versprechen zurück, die alle gelöst wird, sobald die Abhängigkeiten über Dojo geladen werden 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; 
      } 
     }; 
    }); 

danach, in Ihrem MapCtrl, können Sie alle rufen die ArcGIS/Dojo FNS direkt (wie Sie würde normalerweise) durch die Schlüssel, die Sie in dem deps-Objekt verwendet haben, das während App-Konfiguration erstellt wurde, da sie nun an das Objekt, das von wish.get() zurückgegeben wird, angefügt werden.

Was folgt, ist eine modifizierte Version des Beispiels hier (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); 
    }); 

Arbeits Geige den obigen Code demonstriert, hier http://jsfiddle.net/technicolorenvy/2Ke62/4/

+0

ich Ihre Lösung gefällt, aber ich habe ein kleines Problem. Ich benutze angular translate und es dauert einen Bruchteil einer Sekunde, um init.js von "//js.arcgis.com/3.14" zu laden, was dazu führt, dass angular translate als {{'etwas' | translate}} anstelle des gerenderten Textes erscheint bis init.js geladen wird, um 'require' zu laden. Kennen Sie einen Weg, die Abhängigkeit im Wunschdienst zu entfernen? – laitha0

+0

Ich würde dies in Ihrem UI über Preloader oder "Daten geladen" Flags behandeln. eine ziemlich anständige Erklärung des Problems und mögliche Lösung hier http://www.code-hound.com/add-a-preloader-to-your-website-using-angularjs/ – technicolorenvy

+0

Danke, ich werde das ausprobieren. – laitha0

7

Es sieht so aus, als ob Sie versuchen, Ihre Karte in einem direktiven Element zu erstellen. Das ist eine legitime Verwendung, aber ich würde sicherstellen, dass Sie den AMD Loader von Dojo verwenden, um Ihre Module zu laden, und dann bootstrap Ihre Angular App, nachdem all die Dojo-Güte fertig ist.

Ich habe vor kurzem einen Artikel über Angular/Esri dev verfasst, und der Quellcode für ein Beispielprojekt kann gefunden werden.

Was ich tatsächlich mache, ist die Karte von einem Controller zu erstellen, aber der Prozess sollte ähnlich dem Aufbau in der Direktive sein.

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

Ich benutze einen Bootstrap-Modul all meine Angular Bits zu bauen mit dem Esri/Dojo Bits vor dem Winkel Anwendung Bootstrapping.

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

Hoffe, dass hilft ein bisschen.