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:
<!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>
<!--
<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);
}
}
});
});
};
});
</script>
<!--
<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.
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
ngGeocoder
: that you can use to search for addresses on the map ngAddressAutocomplete
: A directive to auto complete address form
And here is a full example with a single html page.
<!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: "=",
markers: "=",
zoom: "@",
mapTypeId: "@",
panControl: "@",
zoomControl: "@",
scaleControl: "@"
},
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" >'+
' <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.