I am currently trying to load multiple google maps on a single page. I don't want to include google map API script into the HTML code as I don't want the script to be loaded unless the maps are in the current page. I want my maps to be called inside a single directive that will also perform the google map API script lazy loading.
So I searched around and found a solution that I tweaked a bit, but my problem is that it will only load one map but not the others.
My HTML looks like this:
<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>
And the directive:
// 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');
});
}
}
}
};
Here is a jsFiddle with 3 maps, you will see that only the last one is loaded:
http://jsfiddle.net/5Pk8f/1/
I guess that I am doing something wrong with my scope or the way the promise is handled, but I am quite out of ideas for now...
Thanks! (and sorry for my not that good english)
Update (after answer)
As an update,
here the the full solution I came up with:
http://plnkr.co/edit/1NpquJ?p=preview (@maurycy plunker)
Google map service
// 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 map directive
// 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 use sample
<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>
Thanks again to maurycy
you have a problem here with promises and initialisation, i've made it cleaner for you
Apparently the jsfiddle has been removed so here is working plunker: http://plnkr.co/edit/1NpquJ?p=preview
js
here is a service for lazy load gmaps
then the directive does what it should do, work only with map, don't load js files on any other logic