Skip to main content

Loading Google Maps API asynchronously with RequireJS

With Single Page Web Applications becoming more and more popular, I decided to understand the concepts of various Javascript frameworks a little better. There are literally hundreds of them, but I decided to start with a really nice tutorial written by Alex Young. Cornerstones in this tutorial are BackboneJS, Underscore, Bootstrap and RequireJS.

After been through the tutorial I decided to roll my own project based on the same setup. I wanted to use Google Maps for this, and searched for a way to load the API using RequireJS. Turned out that there are a few different approaches, but the most common seems to be to use the async-plugin created by Miller Medeiros.

Jason Wyatt has another interesting solution which caught my attention. Being new to all this, I really didn't feel like start involving plug-ins from remote repositories. It might be the most natural thing to do, but one step at a time is more my melody.

Jason's solution had some drawbacks mentioned in the comments of his post, such as the way Google loads itself asynchronously and how the return value will be an empty stub before everything is loaded. I guess the point is to put the actual maps implementation in the anonymous callback function, but I'd rather have more separation between the loading mechanism and the implementation. The same actually applies to the async-plugin solution, as far as I can tell.

I updated Jason's implementation to look like this:

  define(['config'], function(config) {

    function AsyncLoad(callback) {
      // create a global callback method for Google to use
      var callbackName = 'googlemaps'+(new Date()).getTime();
      window[callbackName] = callback;

      // load the api asynchronously
      require(['http://maps.googleapis.com/maps/api/js?key=' + config.apiKey + '&sensor=false&callback=' + callbackName], function(){
        // do nothing and wait for callback instead
      });
    }

    return AsyncLoad;
  });
The way to use it is to instantiate it with a callback function as an argument. And the key is to make this function operate on the calling object and not from the window object, i.e. 'this' should point to the calling object. This is where Function.prototype.bind comes in, as it creates a new function where the 'this' keyword is bound to the supplied value.

This is how to use it:

define(['asyncload'], function(AsyncLoad) {
  var App = function() {
    this.loadMaps();
  };

  App.prototype = {
    loadMaps: function() {
      var asyncLoad = new AsyncLoad(this.renderMaps.bind(this));
    },
    renderMaps: function() {
      console.log(this); // the App
      var mapOptions = {
        zoom: 8,
        center: new google.maps.LatLng(-34.397, 150.644),
        mapTypeId: google.maps.MapTypeId.ROADMAP
      }
    }
  };

  return App;
});
The callback function is bound to the App and the renderMaps-function will be called just as if it was done from the the App itself. Now, the good thing that comes out of all this is that the renderMaps-function holds all code related to Google Maps, no matter how it was loaded.

Comments

  1. Thanks for sharing this tips. Where, I have learned about Google maps. You may also learn about some interesting things in Google Maps. How to Styled Maps or Customize colors in Maps? For More info: http://www.labstech.org/styled-maps-google-maps-2014-06-26/

    ReplyDelete

Post a Comment

Popular posts from this blog

GWT and Spring Security

Update! - Based on the post below, and my other post regarding Spring Security and OpenID, I have added Open-ID support to the sample application below. For those interested, here's the write-up of changes. I've spent quite some time digging into ways of integrating GWT and Spring Security. It all started by reading the following post in the GWT Forum - Best practices/ideas for GWT with Spring Security (or equivalent) , and then checking out this blog - GWT and Spring Security . To make matters worse, I started reading Security for GWT Applications and specifically about the "Cross-Site Request Forging"-attacks. Now, what could I do about it? Well, starting by setting up my own project (Maven-based) with all updated dependencies (GWT 2.0.3 etc) and started reading the Spring Security Reference Documentation (puh!). Instead of See Wah Cheng's approach of implementing a custom authentication service, I decided to rely on standard namespace configuration...

Using Spring Security's OpenID implementation (openid4java) on Google App Engine

The goal with this exercise is to have a running example of an OpenID login on a simple Spring application, using Google as the OpenID Provider. Note that the application will be running on Google App Engine and that Spring Roo is only used for simplicity of creating the project files. Any Spring-based application could use the same implementation. First of all, create a simple project using Spring Roo (or any equivalent framework), including the default security setup: project --topLevelPackage com.technowobble persistence setup --provider DATANUCLEUS --database GOOGLE_APP_ENGINE entity --class ~.domain.MyEntity field string --fieldName name controller all --package com.technowobble.controller security setup This setup only provides us with a form-login, which is not what we wanted. So what about OpenID? Well, if it wasn't for Google App Engine, I would happily have added an <openid-login>-tag to applicationContext-security.xml, but things are never that easy, are the...

Google Apps Script and ES Modules

Currently, Google Apps Script does not support ES modules - and any usage of export/import will fail. One way of handling this is to use rollup.js to bundle your project into one single JavaScript file. The trick here is to make sure not to export any functions in your entry point code, e.g. index.ts , and to prevent any generation of export statement in the final bundle (see the custom rollup plugin in the rollup.config.js below). import { babel } from "@rollup/plugin-babel"; import { nodeResolve } from "@rollup/plugin-node-resolve"; const extensions = [".ts", ".js"]; const preventThreeShakingPlugin = () => { return { name: 'no-threeshaking', resolveId(id, importer) { if (!importer) { // let's not theeshake entry points, as we're not exporting anything in Apps Script files return {id, moduleSideEffects: "no-treeshake" } } return null; }...