AngularJS - асинхронная загрузка скрипта карты Google в директиве для нескольких карт

В настоящее время я пытаюсь загрузить несколько карт Google на одной странице. Я не хочу включать скрипт API карт Google в HTML-код, поскольку я не хочу, чтобы скрипт загружался, если карты не находятся на текущей странице. Я хочу, чтобы мои карты вызывались внутри одной директивы, которая также будет выполнять ленивую загрузку скрипта API карт Google.

Поэтому я поискал и нашел решение, которое я немного подправил, но моя проблема в том, что оно загрузит только одну карту, но не другие.

Мой HTML выглядит так:

<div id="mapParis" class="google-map" lat="48.833" long="2.333"></div>
<div id="mapWashington" class="google-map" lat="38.917" long="-77.000"></div>
<div id="mapTokyo" class="google-map" lat="35.667" long="139.750"></div>

И директива:

// Google Map
app.directive('googleMap', ['$window', '$q', function( $window, $q ) {
    function loadScript() {
        console.log('loadScript');

        // use global document since Angular's $document is weak
        var s = document.createElement('script');
        s.src = '//maps.googleapis.com/maps/api/js?sensor=false&language=en&callback=initMap';
        document.body.appendChild(s);
    }

    // Lazy loading of the script
    function lazyLoadApi(key) {
        console.log('lazyLoadApi');

        var deferred = $q.defer();

        $window.initMap = function () {
            deferred.resolve();
        };

        if ( $window.attachEvent ) {  
            $window.attachEvent('onload', loadScript); 
        } else {
            $window.addEventListener('load', loadScript, false);
        }

        return deferred.promise;
    }

    return {
        restrict: 'C', // restrict by class name
        scope: {
            mapId: '@id', // map ID
            lat: '@',     // latitude
            long: '@'     // longitude
        },
        link: function($scope, elem, attrs) {
            // Check if latitude and longitude are specified
            if ( angular.isDefined($scope.lat) && angular.isDefined($scope.long) ) {
                console.log('-----');

                // Initialize the map
                $scope.initialize = function() {
                    console.log($scope.mapId);

                    $scope.location = new google.maps.LatLng($scope.lat, $scope.long);

                    $scope.mapOptions = {
                        zoom: 6,
                        center: $scope.location
                    };

                    $scope.map = new google.maps.Map(document.getElementById($scope.mapId), $scope.mapOptions);

                    new google.maps.Marker({
                        position: $scope.location,
                        map: $scope.map,
                    });
                }

                // Check if google map API is ready to run
                if ( $window.google && $window.google.maps ) {
                    console.log('gmaps already loaded');

                    // Google map already loaded
                    $scope.initialize();
                } else {
                    lazyLoadApi().then(function () {
                        // Promised resolved
                        console.log('promise resolved');

                        if ( $window.google && $window.google.maps ) {
                            // Google map loaded
                            console.log('gmaps loaded');

                            $scope.initialize();
                        } else {
                            // Google map NOT loaded
                            console.log('gmaps not loaded');
                        }
                    }, function () {
                        // Promise rejected
                        console.log('promise rejected');
                    });
                }
            }
        }
    };

Вот jsFiddle с 3 картами, вы увидите, что загружена только последняя:
http://jsfiddle.net/5Pk8f/1/

Я предполагаю, что я делаю что-то не так с моей областью действия или с тем, как обрабатывается обещание, но сейчас у меня совершенно нет идей...

Спасибо! (и извините за мой не очень хороший английский)


Обновление (после ответа)

В качестве обновления, вот полное решение, которое я придумал:
http://plnkr.co/edit/1NpquJ?p=preview (@maurycy plunker)

Картографический сервис Google

// Lazy loading of Google Map API
app.service('loadGoogleMapAPI', ['$window', '$q', 
    function ( $window, $q ) {

        var deferred = $q.defer();

        // Load Google map API script
        function loadScript() {  
            // Use global document since Angular's $document is weak
            var script = document.createElement('script');
            script.src = '//maps.googleapis.com/maps/api/js?sensor=false&language=en&callback=initMap';

            document.body.appendChild(script);
        }

        // Script loaded callback, send resolve
        $window.initMap = function () {
            deferred.resolve();
        }

        loadScript();

        return deferred.promise;
    }]);

Директива карты Google

// Google Map
app.directive('googleMap', ['$rootScope', 'loadGoogleMapAPI', 
    function( $rootScope, loadGoogleMapAPI ) {  

        return {
            restrict: 'C', // restrict by class name
            scope: {
                mapId: '@id', // map ID
                lat: '@',     // latitude
                long: '@'     // longitude
            },
            link: function( $scope, elem, attrs ) {

                // Check if latitude and longitude are specified
                if ( angular.isDefined($scope.lat) && angular.isDefined($scope.long) ) {

                    // Initialize the map
                    $scope.initialize = function() {                                        
                        $scope.location = new google.maps.LatLng($scope.lat, $scope.long);

                        $scope.mapOptions = {
                            zoom: 12,
                            center: $scope.location
                        };

                        $scope.map = new google.maps.Map(document.getElementById($scope.mapId), $scope.mapOptions);

                        new google.maps.Marker({
                            position: $scope.location,
                            map: $scope.map,
                        });
                    }

                    // Loads google map script
                    loadGoogleMapAPI.then(function () {
                        // Promised resolved
                        $scope.initialize();
                    }, function () {
                        // Promise rejected
                    });
                }
            }
        };
    }]);

Пример использования HTML

<div id="mapParis" class="google-map" lat="48.833" long="2.333"></div>
<div id="mapWashington" class="google-map" lat="38.917" long="-77.000"></div>
<div id="mapTokyo" class="google-map" lat="35.667" long="139.750"></div>

Еще раз спасибо maurycy


person Niflhel    schedule 16.06.2014    source источник


Ответы (1)


у вас есть проблема с промисами и инициализацией, я сделал это для вас чище

По-видимому, jsfiddle был удален, поэтому вот рабочий плункер: http://plnkr.co/edit/1NpquJ?p=preview

js

вот сервис для ленивой загрузки gmaps

app.service('lazyLoadApi', function lazyLoadApi($window, $q) {
  function loadScript() {
    console.log('loadScript')
    // use global document since Angular's $document is weak
    var s = document.createElement('script')
    s.src = '//maps.googleapis.com/maps/api/js?sensor=false&language=en&callback=initMap'
    document.body.appendChild(s)
  }
  var deferred = $q.defer()

  $window.initMap = function () {
    deferred.resolve()
  }

  if ($window.attachEvent) {
    $window.attachEvent('onload', loadScript)
  } else {
    $window.addEventListener('load', loadScript, false)
  }

  return deferred.promise
});

тогда директива делает то, что должна делать, работает только с картой, не загружает файлы js по какой-либо другой логике

person maurycy    schedule 16.06.2014
comment
Спасибо, это работает отлично. Мне просто интересно, почему это не работает, как в моей предыдущей скрипке, с точки зрения логики (это помогло бы мне лучше понять Angular). - person Niflhel; 16.06.2014
comment
Если вы запустите свой планкер, вы увидите в журнале консоли, что все 3 директивы инициализированы, но только одно обещание (последнее) было разрешено, и запустите $scope.initialize() - person maurycy; 16.06.2014
comment
скрипка не работает ни в одном из примеров. Не могли бы вы, ребята, привести полный пример - person InfoStatus; 20.08.2015
comment
$window.initMap функция не распознает deferred, потому что она не входит в ее область действия. - person Aakash; 17.10.2015
comment
Пожалуйста, обновите скрипт, чтобы увидеть его в действии, ссылка не работает. - person alphapilgrim; 29.01.2016
comment
Вы можете использовать $window.onload = loadScript(); - person Andres Separ; 05.04.2016
comment
Большое спасибо ... решил мою проблему. Очень просто и эффективно. Нет необходимости включать какую-либо дополнительную библиотеку. - person tarekahf; 29.08.2017