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
1 | google.maps.Map |
class and add in my own functions, e.g.
1 | 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:
1 2 | 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
1 | require.min.js |
file, after the main block of minified code. Here’s an edited version, with the asynch plugin on line 6:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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
1 | map.js |
file, which, via the
1 | define |
function, we’ll pass UnderscoreJS and the Google Maps JS code using the Asynch plugin. This file is going to return a
1 | Map |
object, which we can then use to instantiate new instances of the class. The file starts off with a
1 | 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
1 | google.maps.Map |
, set the map up and return the variable as output from the function. This way when you do
1 | var myMap = new Map(); |
, your
1 | myMap |
will be able to call all of
1 | google.maps.Map |
‘s functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | define( ['underscore', 'async!https://maps.googleapis.com/maps/api/js?sensor=false'], function(_) { var Map = function (div, lat, lng) { zoom: 15, center: new google.maps.LatLng(lat, lng) }); return map; } // more code below to follow... return Map; }); |
We also need to give our
1 | Map |
‘s protoype the
1 | google.maps.Map |
prototype, which is simply:
1 | Map.prototype = google.maps.Map.prototype; |
Finally, if we want to add our own functions to our
1 | Map |
, we use UnderscoreJS to extend the
1 | Map.prototype |
with our functions. So, for example, I have one called
1 | isBigDrag |
to detect if the user has dragged the map a reasonable amount to fetch more data, as well as
1 | showLoading |
and
1 | hideLoading |
to show/hide my nice ‘Loading…’ div.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | _.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
1 | baseUrl |
above.
To pass this new
1 | Map |
class into a script and to be able to do
1 | var myMap = new Map(); |
, we do so via a call to requirejs in the script, as follows:
1 2 3 4 5 6 7 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | define( ['underscore', 'async!https://maps.googleapis.com/maps/api/js?sensor=false'], function(_) { var Map = function (div, lat, lng) { 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; }); |