AngularJS + Leaflet – 在服务上初始化传单地图

AngularJS + Leaflet – Initialise a leaflet map on a service

我正在 AngularJS 中构建一个应用程序,它使用 LeafletJS 与地图进行交互,提供不同的可能的交互,这些交互被我称为阶段。对于这些阶段中的每一个,都有一个 UIRouter 状态及其控制器和模板。

我目前正在通过服务提供传单功能。这个想法是让该服务初始化传单地图并提供对状态控制器的一些有限访问。因此,这些控制器将调用 setupMarkersInteractions 等服务函数来设置回调,例如在地图上启用标记放置。

但是,在通过 Leaflet 的 leaflet.map() 函数初始化地图时,我 运行 遇到了问题,即:Error: Map container not found。这与 Leaflet 无法找到应该关联地图的 HTML 元素有关。

目前,我有点这样做:

function mapService() { 
  var map;

  return {
    initializeMap : initializeMap,
    setupMarkersInteractions : setupMarkersInteractions
  };

  function initializeMap() {
    map = leaflet.map('map');
  }

  function setupMarkersInteractions() {
    map.on('click', markerPlacementCallback);
  }
}

initializeMap 函数告诉 leaflet.map 查找具有 id='map' 的 HTML 元素,该元素在状态模板上声明。

现在,对于实际问题,这是否与某种 AngularJS 服务无法访问 HTML 有关template? 我找不到任何关于此事的信息,但我认为服务不直接访问视图是有意义的...
如果是,我应该探索什么样的解决方法?我已经研究了 leaflet-directive,但它似乎没有提供添加和删除自定义回调的可能性,并且我希望如此灵活(当我使用 Leaflet-Freedraw 添加免费绘制功能时,事情变得复杂,因为例如)。

我考虑过直接使用 leaflet.map 和元素的 HTMLElement 参数,但我仍然无法让它工作 - 尽管我有可能没有传递应该传递的内容.

发生的事情是,在 L.Map 尝试从您的服务访问 DOM 时,模板仍然可用。通常服务被加载并注入到控制器中,控制器初始化它们的范围,之后模板被初始化并添加到 DOM。你会看到你是否会在你的地图初始化上设置一个大的超时,它会发现它是 DOM 元素。但这是一个非常丑陋的黑客。在 Angular 中,您应该使用指令向 DOM 元素添加逻辑。

例如,一个模板:<leaflet></leaflet> 它是非常基本的指令:

angular.module('app').directive('leaflet', [
  function () {
    return {
      replace: true,
      template: '<div></div>',
      link: function (scope, element, attributes) {
        L.map(element[0]);
      }
    };
  }
]);

您可以将其连接到您的服务并将该元素传递给您的初始化方法:

angular.module('app').directive('leaflet', [
           'mapService'
  function (mapService) {
    return {
      replace: true,
      template: '<div></div>',
      link: function (scope, element, attributes) {
        mapService.initializeMap(element[0]);
      }
    };
  }
]);

这样 initializeMap 方法只会在实际的 DOM 元素可用时被调用。但它给你带来了另一个问题。在您的控制器初始化时,您的服务尚未准备就绪。你可以通过使用承诺来解决这个问题:

angular.module('app').factory('leaflet', [
             '$q',
    function ($q) {
        var deferred = $q.defer();
        return {
          map: deferred.promise,
          resolve: function (element) {
            deferred.resolve(new L.Map(element));
          }
        }
    }
]);

angular.module('app').directive('leaflet', [
           'leaflet',
  function (leaflet) {
    return {
      replace: true,
      template: '<div></div>',
      link: function (scope, element, attributes) {
        leaflet.resolve(element[0]);
      }
    };
  }
]);

如果你想在你的控制器中使用地图实例,你现在可以等到它被解决:

angular.module('app').controller('rootController', [
           '$scope', 'leaflet',
  function ($scope,   leaflet) {
    leaflet.map.then(function (map) {
      var tileLayer = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
        maxZoom: 18
      }).addTo(map);
      map.setView([0, 0], 1);
      L.marker([0, 0]).addTo(map);
    });
  }
]);

这是 Plunker 上的概念示例:http://plnkr.co/edit/DoJpGqtR7TWmKAeBZiYJ?p=preview