Extending Google Maps class with RequireJS and Underscore

24th January, 2015 - Posted by david

I’ve been working alot with RequireJS lately and love the whole modular JS movement, where blocks of code are safely contained within separate functions, that can be loaded in when necessary, without worrying about variable/function name clashes. Generally each module is stored in it’s own file but you can combine several modules into one giant file, in theory containing your entire JS code for your site!

When working with Google Maps, I was thinking it’d be nice to simply extend the google.maps.Map class and add in my own functions, e.g. showLoading to display a nice, semi-transparent ‘Loading…’ div in the centre of the map while the code does an AJAX request to get some data. So, you’d end up with something like:

var map = new Map();
map.showLoading();

So, first thing you need to do is add the Asynch plugin for requireJS to your requireJS config. For external plugins, I tend to source them from https://cdnjs.com/ if possible, cutting down on some of my own server bandwidth. I have my RequireJS config in my require.min.js file, after the main block of minified code. Here’s an edited version, with the asynch plugin on line 6:

requirejs.config({
  'baseUrl': '//cdn.mydomain.com/js',
  'paths': {
    'jquery': 'libs/jquery.min',
    'underscore': 'libs/underscore.min',
    'async': '//cdnjs.cloudflare.com/ajax/libs/requirejs-async/0.1.1/async'
  },
  'shim': {
    'underscore': {
        exports: '_'
    },
  }
});

Next up, we have a map.js file, which, via the define function, we’ll pass UnderscoreJS and the Google Maps JS code using the Asynch plugin. This file is going to return a Map object, which we can then use to instantiate new instances of the class. The file starts off with a Map function, which takes as a parameter the id of the div you want to put the map into and the latitude & longitude of the map’s centre. Obviously you could pass more parameters if you wish, e.g. initial zoom level etc. This Map function will then create a variable of type google.maps.Map, set the map up and return the variable as output from the function. This way when you do var myMap = new Map();, your myMap will be able to call all of google.maps.Map‘s functions.

define(
  ['underscore', 'async!https://maps.googleapis.com/maps/api/js?sensor=false'],
  function(_) {

    var Map = function (div, lat, lng) {
      var map = new google.maps.Map({
        zoom: 15,
        center: new google.maps.LatLng(lat, lng)
      });

      return map;
    }

    // more code below to follow...

    return Map;
});

We also need to give our Map‘s protoype the google.maps.Map prototype, which is simply:

Map.prototype = google.maps.Map.prototype;

Finally, if we want to add our own functions to our Map, we use UnderscoreJS to extend the Map.prototype with our functions. So, for example, I have one called isBigDrag to detect if the user has dragged the map a reasonable amount to fetch more data, as well as showLoading and hideLoading to show/hide my nice ‘Loading…’ div.

    _.extend(Map.prototype, {
      isBigDrag: function (prevCentre) {
        var currentCentre = this.getCenter();
        // .. etc
      },

      showLoading: function() {
        // etc.
      },

      hideLoading: function() {
        // etc.
      }
    });

See below for the entire file, which is stored in ‘classes/map.js’ in baseUrl above.

To pass this new Map class into a script and to be able to do var myMap = new Map();, we do so via a call to requirejs in the script, as follows:

requirejs([
  'underscore', 'classes/map',
], function(_, Map) {
  var myMap = new Map('id', 50.0, 10.0);
  myMap.showLoading();
  // etc.
});

Took me a while to figure it all out but works pretty well now. The classes/maps.js file in it’s entirety is:

define(
  ['underscore', 'async!https://maps.googleapis.com/maps/api/js?sensor=false'],
  function(_) {

    var Map = function (div, lat, lng) {
      var map = new google.maps.Map({
        zoom: 15,
        center: new google.maps.LatLng(lat, lng)
      });

      return map;
    }

    Map.prototype = google.maps.Map.prototype;

    _.extend(Map.prototype, {
      isBigDrag: function (prevCentre) {
        var currentCentre = this.getCenter();
        // .. etc
      },

      showLoading: function() {
        // etc.
      },

      hideLoading: function() {
        // etc.
      }
    });

    return Map;

});

Tags: google maps javascript requirejs underscore underscorejs | david | 24th Jan, 2015 at 11:30am | No Comments

No Comments

Leave a reply

You must be logged in to post a comment.