【问题标题】:AngularJS + Leaflet – Initialise a leaflet map on a serviceAngularJS + Leaflet – 初始化服务上的传单地图
【发布时间】:2015-11-30 12:55:12
【问题描述】:

我正在 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 模板有关?我找不到任何关于此事的信息,但我认为服务不直接访问视图是有意义的...
如果是,我应该探索什么样的解决方法?我已经查看了leaflet-directive,但它似乎没有提供以我想要的灵活性添加和删除自定义回调的可能性(例如,当我使用 Leaflet-Freedraw 添加免费绘图功能时,事情变得复杂了) .

我考虑过直接使用 leaflet.mapHTMLElement 参数作为元素,但我仍然无法使其工作 - 尽管有可能我没有通过应有的内容。

【问题讨论】:

    标签: angularjs leaflet angular-leaflet-directive


    【解决方案1】:

    发生的情况是,当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 元素可用时被调用。但它给你带来了另一个问题。在你的控制器初始化的那一刻,你的服务还没有准备好。你可以通过使用 promise 来解决这个问题:

    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

    【讨论】:

    • 谢谢!我忘了提到我已经尝试过使用自定义指令,就像你在这里写的一样;但是,当时,我无法在单元测试中对其进行测试,最终我忘记了该选项。不过,承诺部分是我从未想过的……我会立即尝试实现这一点!
    • 这里还有超级简单的传单指令:github.com/CleverMaps/tiny-leaflet-directive
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-28
    • 2013-11-07
    • 2012-06-26
    • 2013-10-11
    相关资源
    最近更新 更多