title: AngularJS - load google map script async in directive for multiple maps
body: <p>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.</p>
  
  <p>So I searched around and <a href="https://gist.github.com/neilsoult/7255583" rel="nofollow noreferrer">found a solution</a> that I tweaked a bit, but my problem is that it will only load one map but not the others.</p>
  
  <p>My HTML looks like this:</p>
  
  <pre><code>&lt;div id="mapParis" class="google-map" lat="48.833" long="2.333"&gt;&lt;/div&gt;
  &lt;div id="mapWashington" class="google-map" lat="38.917" long="-77.000"&gt;&lt;/div&gt;
  &lt;div id="mapTokyo" class="google-map" lat="35.667" long="139.750"&gt;&lt;/div&gt;
  </code></pre>
  
  <p>And the directive:</p>
  
  <pre><code>// 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&amp;language=en&amp;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) &amp;&amp; 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 &amp;&amp; $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 &amp;&amp; $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');
                      });
                  }
              }
          }
      };
  </code></pre>
  
  <p>Here is a jsFiddle with 3 maps, you will see that only the last one is loaded: <br><a href="http://jsfiddle.net/5Pk8f/1/" rel="nofollow noreferrer">http://jsfiddle.net/5Pk8f/1/</a></p>
  
  <p>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...</p>
  
  <p>Thanks! (and sorry for my <em>not that good</em> english)</p>
  
  <hr>
  
  <h1>Update (after answer)</h1>
  
  <p>As an update,
  here the the full solution I came up with:<br>
  <a href="http://plnkr.co/edit/1NpquJ?p=preview" rel="nofollow noreferrer">http://plnkr.co/edit/1NpquJ?p=preview</a> (@maurycy plunker)</p>
  
  <h2>Google map service</h2>
  
  <pre><code>// 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&amp;language=en&amp;callback=initMap';
  
              document.body.appendChild(script);
          }
  
          // Script loaded callback, send resolve
          $window.initMap = function () {
              deferred.resolve();
          }
  
          loadScript();
  
          return deferred.promise;
      }]);
  </code></pre>
  
  <h2>Google map directive</h2>
  
  <pre><code>// 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) &amp;&amp; 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
                      });
                  }
              }
          };
      }]);
  </code></pre>
  
  <h2>HTML use sample</h2>
  
  <pre><code>&lt;div id="mapParis" class="google-map" lat="48.833" long="2.333"&gt;&lt;/div&gt;
  &lt;div id="mapWashington" class="google-map" lat="38.917" long="-77.000"&gt;&lt;/div&gt;
  &lt;div id="mapTokyo" class="google-map" lat="35.667" long="139.750"&gt;&lt;/div&gt;
  </code></pre>
  
  <p>Thanks again to maurycy</p>