22
\$\begingroup\$

Here is a simple reusable AngularJS factory I came up with to initialize Google Maps Asynchronously, which somehow does not seem to be supported by major libraries (see below):

Following Google's guidelines I am using global callback function (which seems to be unavoidable harm), that resolves a dummy promise, which is the only API returned.

Then maps can be loaded anywhere by simply calling

Initializer.mapsInitialized.
    .then(function(){
        map = new google.maps.Map(element, options);
    });

Below is the code. Any critics, potential flaws, drawbacks, violation of best practices is welcome!

// Google async initializer needs global function, so we use $window
angular.module('GoogleMapsInitializer')
    .factory('Initializer', function($window, $q){

        // maps loader deferred object
        var mapsDefer = $q.defer();

        // Google's url for async maps initialization accepting callback function
        var asyncUrl = 'https://maps.googleapis.com/maps/api/js?callback=';

        // async loader
        var asyncLoad = function(asyncUrl, callbackName) {
          var script = document.createElement('script');
          //script.type = 'text/javascript';
          script.src = asyncUrl + callbackName;
          document.body.appendChild(script);
        };

        // callback function - resolving promise after maps successfully loaded
        $window.googleMapsInitialized = function () {
            mapsDefer.resolve();
        };

        // loading google maps
        asyncLoad(asyncUrl, 'googleMapsInitialized');

        return {

            // usage: Initializer.mapsInitialized.then(callback)
            mapsInitialized : mapsDefer.promise
        };
    })

Most major libraries built for Google Maps seem to use the standard script tag

<script src='//maps.googleapis.com/maps/api/js'></script>

which is, however, blocking, may result in delays, and makes the page unsuitable for offline testing.

On another note, library such as script.js async loader, does not seem to work with this particular script.

\$\endgroup\$
13
\$\begingroup\$

I like it,

you could clean up the commented out code and possibly replace

    $window.googleMapsInitialized = function () {
        mapsDefer.resolve();
    };

with

    $window.googleMapsInitialized = mapsDefer.resolve;

Personally, I would re-arrange the code a tiny bit, keep the URL completely on top, keep asyncLoad and the call closer together. Keep the declaration of mapsDefer and the resolving function closer.

// Google async initializer needs global function, so we use $window
angular.module('GoogleMapsInitializer')
.factory('Initializer', function($window, $q){

    //Google's url for async maps initialization accepting callback function
    var asyncUrl = 'https://maps.googleapis.com/maps/api/js?callback=',
        mapsDefer = $q.defer();

    //Callback function - resolving promise after maps successfully loaded
    $window.googleMapsInitialized = mapsDefer.resolve; // removed ()

    //Async loader
    var asyncLoad = function(asyncUrl, callbackName) {
      var script = document.createElement('script');
      //script.type = 'text/javascript';
      script.src = asyncUrl + callbackName;
      document.body.appendChild(script);
    };
    //Start loading google maps
    asyncLoad(asyncUrl, 'googleMapsInitialized');

    //Usage: Initializer.mapsInitialized.then(callback)
    return {
        mapsInitialized : mapsDefer.promise
    };
})
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.