Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML5

Google Map, JQuery, AngularJS Address Form autocomplete and geocoder

5.00/5 (4 votes)
24 Jan 2016CPOL2 min read 21.7K  
Control and directives for address form auto completion using Google map V3 and JQuery/AngularJS

Introduction

When we have to deal with address forms, usually we have to fill the street name, street number, postal code, etc.

And it’s sometimes repetitive and can take more time to fulfill a simple form.

Here is a simple control using Google map to automatically complete your address forms.

Background

First is JQuery control that you can include in web page that will help a user automatically fill an address form when he begins typing the address.

Second is an Angular module with directives for drawing a map with markers, a directive for geocoder that helps for searching places on Gmap and finally address form auto complete.

Using the Code

JQuery Address Form Auto Complete

Here is a simple JQuery control used with a Google map v3 API that will automatically fill an address form.

You can include it directly in your web page or separately in JavaScript file. This example is with a full page:

HTML
<!DOCTYPE html>
<html   id='ng-app'>
<head>
<link rel="stylesheet" type="text/css" 
href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css">
</head>

<body>
  <form class="form-horizontal " role="form" style="margin:10vh 30%">
    <div class="form-group">
        <label class="control-label col-sm-2 "  
        for="fullAddress">Address:</label>
        <div  class="col-sm-10">
            <input type="text" name="fullAddress" 
            id="fullAddress" class="form-control"  />   
        </div>
    </div>
    <br/>        
    <div class="form-group">
        <label class="control-label  " 
        for="StreetNumber">Street Number:</label>
        <div>
            <input type="text" name="StreetNumber" 
            class="form-control"  data-role="street_number"/> 
        </div>
    </div>        
    <br/>
    <div class="form-group">
        <label class="control-label  " for="Street">Street:</label>
        <div>
            <input type="text" name="Street" 
            class="form-control" data-role="route"/> 
        </div>
    </div>        
    <br/>    
    <div class="form-group">
        <label class="control-label  " for="City">City:</label>
        <div>
            <input type="text" name="City" 
            class="form-control" data-role="administrative_area_level_2"/> 
        </div>
    </div>            
    <br/>
    <div class="form-group">
        <label class="control-label  " for="City">locality:</label>
        <div>
        <input type="text" name="locality" 
        class="form-control" data-role="administrative_area_level_1"/> 
        </div>
    </div>            
    <br/>
    <div class="form-group">
        <label class="control-label  " for="ZipCode">Zip:</label>
        <div>
            <input type="text" name="ZipCode" 
            class="form-control" data-role="postal_code"/> 
        </div>
    </div>
    <br/>        
    <div class="form-group">
        <label class="control-label  " 
        for="Country">Country:</label>
        <div>
            <input type="text" name="Country" 
            class="form-control" data-role="country"/> 
        </div>
    </div>    
   </form>
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.js"></script>
<script src="http://maps.googleapis.com/maps/api/js?key=&sensor=false&extension=.js&libraries=places">
</script>

<!-- include this in separate js file -->
<script>
  $(function (){  
      
    $.fn.googleMapAuocompleteAddress = function(opt) {  
        var options = $.extend({}, {}, opt);   
        return this.each(function(i, e){ 
            if(! $(this).is("input"))return ;
            var from=$(this).closest("form");            
            var autocomplete = new google.maps.places.Autocomplete($(this).get(0));  
            google.maps.event.addListener(autocomplete, 'place_changed', function() {  
                var place = autocomplete.getPlace();  
                var componenents=place.address_components||[];  
                var ln=componenents.length; 
                from.find("input[data-role]").val("");            
                for (var i=0 ; i<ln;i++) {   
                    var editor=$("[data-role|="+componenents[i].types[0]+"]");   
                    if(editor.is("input")){  
                        editor.val(componenents[i].long_name);  
                    }else{  
                        editor.html(componenents[i].long_name);  
                    }//end if   
                }//end for   

            });//end addListener   
        });//end each    
    };//end googleMapAuocompleteAddress  
});  
</script>

<!-- add this on the page -->
<script>
$(function (){  
    $("#fullAddress").googleMapAuocompleteAddress() ;  
});  
</script>
  
</body>

You can take a look at a live demo here.

AngularJS Module for Google map, Geocoder, Address Auto Complete

Here is an AngularJs module with a set of directives that you can inject in your Angular application to get address auto complete, draw Google map and search addresses on the map (geocoder).

There also Angular-ui module for Google map which is more complete but also complex. The code here is simple and you can modify it as per your convenience.

  1. ngMap: This directive is for drawing the map and as parameters you have:
    • markers: An array of markers
    • center: The map center on page load
    • and other parameters that you can see here in the code
  2. ngGeocoder: that you can use to search for addresses on the map
  3. ngAddressAutocomplete: A directive to auto complete address form

And here is a full example with a single html page.

HTML
<!DOCTYPE html>
<html   id='ng-app'  >
<head>
    <link rel="stylesheet" type="text/css" 
    href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css">
</head>
<body  ng-app="app" ng-controller="appCtrl">
 <h3 class="text-center">   Angular, 
 Google Maps autocomplete and geocoder </h3>
<div class="text-center">
        <div class="input-append text-right" 
        ng-geocoder ng-model="address" geo-coord="mapcoord">
        </div>
    </div>
    <div ng-map style="height:400px;margin:12px;box-shadow:0 3px 25px black;"
        center="{ lat: 40, lon: -73 }"
        markers="[]"
        map="myMap"
        geo-coord="mapcoord"> 
    </div>
     <br/> <br/>
    <form class="form-horizontal" role="form">
        <div class="form-group">
            <label class="control-label col-sm-2 "  >Saisir adresse:</label>
            <div  class="col-sm-10">
                <input type="email"  
                class="form-control" ng-model="fullAddress"  
                                        ng-address-autocomplete   
                                        street-name="streetName" 
                                        city="city"
                                        zip-code="zipCode"
                                        country="country"
                                        street-number="streetNumber" />
            </div>
        </div>
        <br/>        
        <div class="form-group">
        <label class="control-label  "  >Numero:</label>
        <div>
            <input type="text" class="form-control" 
            ng-model="streetNumber"/> 
        </div>
        </div>        
        <br/>
        <div class="form-group">
        <label class="control-label  "  >Rue:</label>
        <div>
            <input type="text" 
            class="form-control" ng-model="streetName"/> 
        </div>
        </div>        
        <br/>    
        <div class="form-group">
        <label class="control-label  "  >Ville:</label>
        <div>
            <input type="text" 
            class="form-control" ng-model="city"/> 
        </div>
        </div>            
         <br/>
        <div class="form-group">
        <label class="control-label  "  >Code postal:</label>
        <div>
            <input type="text" 
            class="form-control" ng-model="zipCode"/> 
        </div>
        </div>
        <br/>        
        <div class="form-group">
        <label class="control-label  "  >Pays:</label>
        <div>
            <input type="text" 
            class="form-control" ng-model="country"/> 
        </div>
        </div>    
   </form>
<script src="http://maps.googleapis.com/maps/api/js?key=&
sensor=false&extension=.js&libraries=places">
</script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.16/angular.min.js">
</script>

 <script>
 
 angular.module("ngMapModule", [])
  .directive("ngMap", function ($timeout) {
    return {
        restrict: "EA",
        replace: true,
        template: "<div></div>",
        scope: {
            geoCoord:"=",
            center: "=",        //{latitude: 10, longitude: 10 } .
            markers: "=",       //[{ lat: 10, lon: 10, name: "sName" }] .
            zoom: "@",          //(1  zoomed out, 25  zoomed in).
            mapTypeId: "@",     //(roadmap, satellite, hybrid, terrain).
            panControl: "@",    //show a pan control on the map.
            zoomControl: "@",   //show a zoom control on the map.
            scaleControl: "@"   //show scale control on the map.
        },
        controller:function($scope){
        $scope.updateMap=function(elem) {
                var options =getOptions();
                $scope.map = new google.maps.Map(elem, options);
                $scope.updateMarkers();
                google.maps.event.addListener($scope.map, 'center_changed', function () {
                    if ($scope.initCenter) clearTimeout($scope.initCenter);
                    $scope.initCenter = $timeout(function () {
                        if ($scope.center) {
                            if ($scope.map.center.lat() != $scope.center.lat ||
                                $scope.map.center.lng() != $scope.center.lon) {
                                $scope.center = { lat: $scope.map.center.lat(), 
						  lon: $scope.map.center.lng() };
                                if (!$scope.$$phase) $scope.$apply("center");
                            }
                        }
                    }, 500);
                });
            }
        $scope.updateMarkers=function() {
                if ($scope.map && $scope.markers) {
                    if ($scope.currentMarkers != null) {
                        for (var i = 0; i < $scope.currentMarkers.length; i++) {
                            $scope.currentMarkers[i] = m.setMap(null);
                        }
                    }
                    $scope.currentMarkers = [];
                    var markers = $scope.markers;
                    if (angular.isString(markers)) markers = $scope.$eval($scope.markers);
                    for (var i = 0; i < markers.length; i++) {
                        var m = markers[i];
                        var loc = new google.maps.LatLng(m.lat, m.lon);
                        var mm = new google.maps.Marker({ position: loc, map: $scope.map, title: m.name });
                        $scope.currentMarkers.push(mm);
                    }
                }
            }
        $scope.getLocation=function(loc) {
                if (loc == null) return new google.maps.LatLng(40, -73);
                if (angular.isString(loc)) loc = $scope.$eval(loc);
                return new google.maps.LatLng(loc.lat, loc.lon);
            }
        function getOptions() {
                var options =
                            {
                                center: new google.maps.LatLng(40, -73),
                                zoom: 6,
                                mapTypeId: "roadmap"
                            };
                if ($scope.center) options.center = $scope.getLocation($scope.center);
                if ($scope.zoom) options.zoom = $scope.zoom / 1;
                if ($scope.mapTypeId) options.mapTypeId = $scope.mapTypeId;
                if ($scope.panControl) options.panControl = $scope.panControl;
                if ($scope.zoomControl) options.zoomControl = $scope.zoomControl;
                if ($scope.scaleControl) options.scaleControl = $scope.scaleControl;
                return options;
            }
        },
        link: function (scope, element, attrs) {
        var scopeArray = ["markers", "mapTypeId", 
        "panControl", "zoomControl", "scaleControl"],toCenter;
            for (var i = 0, cnt = scopeArray.length; i < scopeArray.length; i++) {
                scope.$watch(scopeArray[i], function () {
                    cnt--;
                    if (cnt <= 0) {
                        scope.updateMap(element[0] );
                    }
                });
            }
            scope.$watch("zoom", function () {
                if (scope.map && scope.zoom)
                    scope.map.setZoom(scope.zoom / 1);
            });
            scope.$watch("center", function () {
                if (scope.map && scope.center)
                    scope.map.setCenter(scope.getLocation(scope.center));
            });
            scope.$watch("geoCoord", function (val) {
                if (val){
                var coord=new google.maps.LatLng(val.lat, val.lon);
                scope.map.setCenter(coord);
                scope.map.setZoom(12);
                }                    
            });            
        }
    };
}).directive("ngGeocoder", function () {
    return {
        restrict: "EA",
        template: '<input   type="text"  ng-model="ngModel" 
        style="width:500px"/>'+
               '<button class="btn" type="button" 
               ng-click="geoCode()" ng-disabled="address.length == 0" 
               title="address" >'+

              '&nbsp;<i class="icon-search"></i></button>',
        scope: {
            ngModel:"=",
            geoCoord:"="
           },
        controller:function($scope){
             
            $scope.geoCode = function () {
                if ($scope.ngModel && $scope.ngModel.length > 0) {
                    if (!$scope.geocoder) $scope.geocoder = new google.maps.Geocoder();
                    $scope.geocoder.geocode
                    ({ 'address': $scope.ngModel }, function (results, status) {
                        if (status == google.maps.GeocoderStatus.OK) {
                            var location = results[0].geometry.location;
                            if (!$scope.$$phase) $scope.$apply(function(){
                            $scope.geoCoord={ lat: location.lat(), lon: location.lng() }
                            $scope.ngModel = results[0].formatted_address;
                            });
                            
                             
                        } else {
                            alert("Sorry,  address produced no results.");
                        }
                    });
                }
            };            
        },
        link: function (scope, element, attrs) {        
        }
        }
    }).directive("ngAddressAutocomplete", function () {
    return {
        restrict: "EA",
        scope: {
            ngModel:"=",
            city:"=",
            zipCode:"=",
            country:"=",
            streetName:"=",
            streetNumber:"="
           },
        link: function (scope, element, attrs) {
            var autocomplete = new google.maps.places.Autocomplete(element[0]);
            google.maps.event.addListener(autocomplete, 'place_changed', function() {  
            var place = autocomplete.getPlace();  
             
            if (!scope.$$phase) scope.$apply(function(){
                    var componenents=place.address_components||[];
                    for(var i=0;i<componenents.length;i++){
                        var item=componenents[i];
                        if(item&&item.types){
                           if(/route/gi.test(item.types[0])){
                              scope.streetName =componenents[1].long_name;
                           }
                           if(/country/gi.test(item.types[0])){
                              scope.country =item.long_name;
                           }
                           if(/street_number/gi.test(item.types[0])){
                              scope.streetNumber =item.long_name;
                           }
                           if(/postal_code/gi.test(item.types[0])){
                              scope.zipCode =item.long_name;
                           }
                           if(/dministrative_area_level_2|city/gi.test(item.types[0])){
                              scope.city =item.long_name;
                           }
                       }
                    }
            });                        
            })
        }
        }
    })
 var app = angular.module("app", ["ngMapModule"]);
     app.controller("appCtrl", function ($scope) {
     
     $scope.markers=[];
});
 </script>
</body>

You can take a look at the live demo here.

Conclusion

This is a useful control to auto complete an address form and search places on Google map. Hope this will help.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)