2013-07-09 13 views
9

Próbuję użyć ArcGIS JavaScript API wewnątrz aplikacji kątowej. Jak widzę, używa Dojo. Więc próbuję zainicjować ArcGIS z Kątowymi dyrektywy tak: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); 
    } 

Wygląda ten sposób nie jest w 100% poprawne, ponieważ gdy próbuję przybliżyć poprzez przewijanie kółkiem myszy, otrzymuję ten błąd:

Uncaught TypeError: Cannot call method 'apply' of null 

Moje pytanie brzmi: jak prawidłowo wstrzykiwać funkcjonalność ArcGIS w aplikacji Angular?

Odpowiedz

12

Myślę, że podejście w stylu "AngularJS" byłoby podobne do następującego. (skrzypce tutaj http://jsfiddle.net/technicolorenvy/2Ke62/4/)

Lubię używać angular-ui-router, ale to podejście będzie również działać w/Angular's $routeProvider. Magia tutaj znajduje się w obiekcie rozstrzygnięcia, który opcjonalnie "czeka", dopóki obietnica nie zostanie rozwiązana przed kontynuowaniem.

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

Jak widać powyżej, mamy map stanu, który ma resolve rekwizyt. Następnie można zbudować obiekt, który reprezentuje zależności ArcGIS/Dojo i przekazać to do swojego wish.loadDependencies (patrz poniżej).

Korzystanie q wrócimy obietnicę, że zostanie rozwiązany, gdy wszystkie zależności są ładowane poprzez 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; 
      } 
     }; 
    }); 

potem, w MapCtrl, można wywołać wszystkie ArcGIS/Dojo FNS bezpośrednio (jak ty normalnie), za pomocą kluczy użytych w obiekcie deps, który został skonstruowany podczas konfiguracji aplikacji, ponieważ są one teraz dołączone do obiektu zwróconego przez wish.get().

Poniżej znajduje się zmodyfikowana wersja przykład znaleźć tutaj (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); 
    }); 

skrzypce pracy wykazując powyższy kod, tutaj http://jsfiddle.net/technicolorenvy/2Ke62/4/

+0

Lubię swoje rozwiązanie, ale mam mały problem. Używam translacji kątowej i zajmuje ułamek sekundy, aby załadować plik init.js z "//js.arcgis.com/3.14", powodując, że translacja kątowa pojawia się jako {{'coś' | tłumacz}} zamiast renderowanego tekstu dopóki init.js nie załaduje się, aby załadować 'require'. Czy znasz sposób, aby usunąć wymaganą zależność wewnątrz usługi życzeń? – laitha0

+0

Zajmę się tym w twoim interfejsie użytkownika za pomocą preloadera lub flag "załadowanych danych". całkiem przyzwoite wyjaśnienie problemu i możliwe rozwiązanie tutaj http://www.code-hound.com/add-a-preloader-to-your-website-using-angularjs/ – technicolorenvy

+0

Dzięki, spróbuję tego. – laitha0

7

Wygląda na to, że próbujesz zbudować mapę wewnątrz elementu dyrektywy. Jest to uzasadnione użycie, ale chciałbym się upewnić, że używasz programu ładującego AMD dojo, aby załadować moduły, a następnie bootstrap swoją aplikację Angular po tym, jak cała dobroć Dojo jest gotowa.

Niedawno przygotowałem zestawienie dla Angular/Esri, a kod źródłowy dla przykładowego projektu można znaleźć pod numerem here.

Co właściwie robię, to buduję mapę ze sterownika, ale proces powinien być podobny do zbudowania go w dyrektywie.

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

używam moduł ładowania początkowego budować wszystkie moje kątowe bity z wykorzystaniem bitów Esri/Dojo przed ładowanie początkowe kątowego aplikację.

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

Mam nadzieję, że trochę pomaga.