Google Maps API RequireJS Module

This post documents how to create a RequireJS module that loads the Google Maps JavaScript API and is used by a web page to populate form inputs with location data. The Google Map contains a marker that can be dragged to reset the form with the marker position location data.

Source Code

Development Environment

In order to use the package managers, Gulp task runner, BrowserSync and RequireJS optimizer, you will need to have the following installed.

  1. Node.js
  2. Bower

Using a Command shell in the project root, follow these steps to get the local development environment setup.

Create package.json

The package.json file contains project metadata and lists dependencies that are available on npm. This is useful to have when you want to re-install or update the dependencies with the npm install command.

# create package.json
npm init

Install node modules

# install dependencies and save to package.json
npm install browser-sync --save-dev
npm install gulp --save-dev
npm install requirejs --save-dev

Create bower.json

The bower.json file contains project metadata and lists dependencies that are available on bower. Re-install / update bower components saved to this file with the bower install command.

# create bower.json
bower init

Install bower components

Only using the async plugin from this requirejs-plugins bundle for async dependency loading of the Google Maps JavaScript API.

# install bower components and save to bower.json
bower install --save requirejs-plugins

Project Folders and Files

Create these folders and files. Browse, clone or download the source code if you prefer.

  • css
  • js
  • src
    • js
      • modules
        • gmap.js
        • location.js
      • require
      • main.js
    • gulpfile.js
    • index.html
    • require.build.js

Web Page

This web page contains the map container, address and geocode location inputs. jQuery is loaded separately before the require.js module loader to demonstrate how to define jQuery when it is external.

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Page Title</title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>

    <p>Drag the marker to a location on the map and set input values.</p>

    <div id="map-canvas"></div>
    <label>Street
        <input type="text" id="street" name="street" />
    </label>
    <label>Locality
        <input type="text" id="locality" name="locality" />
    </label>
    <label>Region
        <input type="text" id="region" name="region" />
    </label>
    <label>Country
        <input type="text" id="country" name="country" />
    </label>
    <label>Latitude
        <input type="text" id="latitude" name="latitude" />
    </label>
    <label>Longitude
        <input type="text" id="longitude" name="longitude" />
    </label>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>

    <!-- Prod / built version -->
    <!--script data-main="/js/main" src="/js/require/require.js"></script-->
    <!-- Dev version -->
    <script data-main="/src/js/main" src="/src/js/require/require.js"></script>

</body>
</html>

Google Map JavaScript API

This module is dependent upon the RequireJS async plugin to load the google maps api. The gmap function accepts three parameters: the default latitude, default longitude and a callback function to handle the result data.

gmap.js
define(['async!//maps.googleapis.com/maps/api/js'],
    function(async){
    var gmap = {
        map: {},
        init: function gmap(lat, lng, callback) {
            var geocoder = new google.maps.Geocoder();
            var marker = new google.maps.Marker({
                position: new google.maps.LatLng(lat, lng),
                anchorPoint: new google.maps.Point(0, -29),
                draggable: true
            });
            map = new google.maps.Map(document.getElementById('map-canvas'), {zoom: 10});
            map.setCenter(marker.position);
            marker.setMap(map);

            google.maps.event.addListener(marker, 'dragend', function (evt) {

                var latlng = {lat: evt.latLng.lat(), lng: evt.latLng.lng()};
                var addrComponents = {
                    street_number: 'short_name',
                    route: 'long_name',
                    establishment: 'long_name',
                    locality: 'long_name',
                    administrative_area_level_1: 'short_name',
                    country: 'short_name',
                    postal_code: 'short_name'
                };
                result = {
                    address: {},
                    latitude: 0,
                    longitude: 0
                };

                geocoder.geocode({'location': latlng}, function(results, status) {
                    if (status === google.maps.GeocoderStatus.OK) {
                        if (results[0]) {

                            var i; var type = null;
                            for (i = 0; i < results[0].address_components.length; i++) {
                                type = results[0].address_components[i].types[0];
                                if (addrComponents[type]) {
                                    result.address[type] = results[0].address_components[i][addrComponents[type]];
                                }
                            }
                            result.latitude = latlng.lat;
                            result.longitude = latlng.lng;
                            if(typeof callback == "function"){
                                callback(result);
                            }

                        } else {
                            window.alert('No results found');
                        }
                    } else {
                        window.alert('Geocoder failed due to: ' + status);
                    }
                });
            });
        }
    };
    return gmap;
});

Location module

This module handles the result data from the gmap.js module and sets the input values.

location.js
define(['jquery','modules/gmap'],
    function($, gmap){
    var location = {
        // default map coordinates
        latitude: 39.084014922903,
        longitude: -77.51372591791,
        init: function(){
            gmap.init(
                location.latitude,
                location.longitude,
                function(result){
                    console.log('RESULT',result);

                    if (result.address) {
                        location._setAddress(result.address);
                    }

                    $('#latitude').val(result.latitude);
                    $('#longitude').val(result.longitude);
                }
            );
        },
        _setAddress: function(address) {
            var street = [];
            if (address.street_number) {
                street.push(address.street_number);
            }
            if (address.route) {
                street.push(address.route);
            }
            if (street.length > 0) {
                $('#street').val(street.join(' '));
            }
            if (address.locality) {
                $('#locality').val(address.locality);
            }
            if (address.administrative_area_level_1) {
                $('#region').val(address.administrative_area_level_1);
            }
            if (address.country) {
                $('#country').val(address.country);
            }
        }
    };
    location.init();
    return location;
});
main.js

JavaScript application entry point.

'use strict';

// since jQuery is already loaded from google cdn
define('jquery', [], function() {
    return jQuery;
});

require.config({
    paths : {
        //create alias to plugins (not needed if plugins are on the baseUrl)
        async: 'require/async'
    }
});

require(['modules/location'], function(location) {
        location.init();
});
gulpfile.js

This gulp file defines two tasks. 1. gulp copy copies node modules and bower components into the application. 2. gulp server runs the application in a static BrowserSync web server.

'use strict';
var gulp = require('gulp');
var browserSync = require('browser-sync').create();

// copy vendor libraries into app
gulp.task('copy', function() {
    gulp.src([
        './bower_components/requirejs-plugins/src/async.js',
        './node_modules/requirejs/require.js'
        ])
        .pipe(gulp.dest('./src/js/require/'));
});

// BrowserSync static server
gulp.task('server', function() {
    browserSync.init({
        server: {
            baseDir: './',
            directory: false,
            index: 'index.html'
        }
    });
});
require.build.js

This is the RequireJS Optimizer build configuration file. Once the entry point and modules are created, run node node_modules/requirejs/bin/r.js -o require.build.js to compile the modules into a single js/main.js file.

({
    baseUrl: './src/js',
    dir: './js',
    modules: [
        {
            name: 'main'
        }
    ],
    fileExclusionRegExp: /^(r|require.build)\.js$/,
    removeCombined: true,
    paths: {
        async: 'require/async'
    },
    preserveLicenseComments: false
})
style.css

Add this file into the css folder to declare some basic styles.

label {
    display: block;
    width: 300px;
    margin-bottom: 1em;
}
input[type='text'] {
    display: block;
    width: 100%;
}
#map-canvas {
    width: 300px;
    height: 300px;
    margin-bottom: 1em;
}

Overview

  1. Create package.json and bower.json
  2. Install node modules and bower components
  3. Create /index.html
  4. Create /src/js/modules/gmap.js
  5. Create /src/js/modules/location.js
  6. Create /src/js/main.js
  7. Create /gulpfile.js
  8. Create /require.build.js
  9. Create /css/style.css
  10. Copy the node modules and bower components into the application. In a command shell, run gulp copy
  11. Load the application in the web browser. In a command shell, run gulp server
  12. Optimize the application. In a command shell, run node node_modules/requirejs/bin/r.js -o require.build.js

Reference


Part 1 of 3 in the Google Maps API series.

Google Maps API with Browserify

comments powered by Disqus