Skip to content

Instantly share code, notes, and snippets.

@victorb
Created September 24, 2013 16:37
Show Gist options
  • Star 79 You must be signed in to star a gist
  • Fork 22 You must be signed in to fork a gist
  • Save victorb/6687484 to your computer and use it in GitHub Desktop.
Save victorb/6687484 to your computer and use it in GitHub Desktop.
Easy AngularJS Directive for Google Places Autocomplete
var myApp = angular.module('myApp', []);
myApp.directive('googleplace', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, model) {
var options = {
types: [],
componentRestrictions: {}
};
scope.gPlace = new google.maps.places.Autocomplete(element[0], options);
google.maps.event.addListener(scope.gPlace, 'place_changed', function() {
scope.$apply(function() {
model.$setViewValue(element.val());
});
});
}
};
});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.gPlace;
}
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?libraries=places&sensor=false"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<div>Google Places Autocomplte integration in Angular</div>
<div>To Test, start typing the name of any Indian city</div>
<div>selection is: {{chosenPlace}}</div>
<div><input ng-model="chosenPlace" googleplace/></div>
</div>
@tapan-p
Copy link

tapan-p commented Sep 23, 2015

I am not sure what exactly I am missing here but i tried modifying the code little bit as below and it is not displaying any data. Can someone please help?

<! DOCTYPE html>
<html> 
<head>

<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?libraries=places&sensor=false"></script>
</head>
<body>
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="app.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
    <div>Google Places Autocomplte integration in Angular</div>
    <div>To Test, start typing the name of any Indian city</div>
    <div>selection is: {{chosenPlace}}</div>
    <div><input ng-model="chosenPlace" googleplace/></div>
</div>
</body>
</html>

Copy link

ghost commented Oct 1, 2015

Every time the directive is used, google injects a css (inside head).
If the user remains in the website and visited this component several time, he will have several injected css.
Anyone found a solution for this ? is possible to avoid the CSS injection ?
Thanks

@chaitz24
Copy link

Thanks it worked smooth.

@medindm
Copy link

medindm commented Nov 3, 2015

Thanks @FDIM! Great help!

@hussaino
Copy link

Wow, thanks a bunch :)

@gadjetboi
Copy link

awesome. thanks man.

@kpnigalye
Copy link

Thanks @wallawe that was really helpful. Also I replaced 'cities' with 'geocode' in options to get other information.
var options = { types: ['geocode'] };

@vkilmore
Copy link

vkilmore commented Feb 7, 2016

This is a great directive, I have a question though, when I click on the dropdown list to select one of the suggestions it appears that the dropdown does take the click and instead whatever is behind the dropdown gets selected. Any ideas?

@stormdragon
Copy link

Thanks @FDIM!
I have got Great help!
Then, i have a problem on mobile. i cant click the dropdown items. Do you have any good idea?

@bialesdaniel
Copy link

I have the same issue as @stormdragon. Any suggestions?

@apapatp
Copy link

apapatp commented Mar 29, 2016

Dude awesome sauce!!

@udarapathmin
Copy link

Awesome work..Thanks

@japharr
Copy link

japharr commented May 26, 2016

Thanks @IvanStone

@NateJBeck
Copy link

Awesome job! I use this in many forms to set important address info after address selection - no parsing required - might be useful if you want to do the same:

<myApp-google-autocomplete ng-model="fullAddress" zip="vm.model.zipcode"
    country="vm.model.country" state="vm.model.state" city="vm.model.city"
    address1="vm.model.address_1">
</myApp-google-autocomplete>

.directive('myAppGoogleAutocomplete', function() {
    return {
        require: 'ngModel',
        replace: true,
        scope: {
            ngModel: '=',
            address1: "=",
            city: "=",
            state: "=",
            country: "=",
            zip: '=',
        },
        template: '<input class="form-control" type="text">',
        link: function(scope, element, attrs, model) {
            var options = {
                types: [],
                componentRestrictions: {}
            };    

            var autocomplete = new google.maps.places.Autocomplete(element[0], options);

            google.maps.event.addListener(autocomplete, 'place_changed', function() {
                scope.$apply(function() {
                    var place = autocomplete.getPlace();
                    var components = place.address_components;  // from Google API place object   

                    scope.address1 = components[0].short_name + " " + components[1].short_name;
                    scope.city = components[3].short_name;
                    scope.state = components[5].short_name;
                    scope.country = components[6].long_name;
                    scope.zip = components[7].short_name;

                    model.$setViewValue(element.val());   
                });
            });
        }
    }
})

@code-storm
Copy link

You saved the day.
<pre>Thanks</pre>

@nirajbothra
Copy link

the original solution worked like a charm..

Thanks

@Henryisthebest
Copy link

Hi,
I'm using ionic - and I am trying to pass the lat long to a function when I click a button on the page. How do I access the lat long? my ng-model variable is 'chosenPlace', and in my controller I have defined $scope.chosenPlace, however when I try retrieve the value it is undefined?

@exaland
Copy link

exaland commented Oct 19, 2016

Hi , Please help me i want use this script with 2 Input text box -> Pickup Location and Destination

here my script :

angular.module('app.directives', [])
.directive('googleplace', function() {
    return {
        require : 'ngModel',
        link : function(scope, element, attrs, model) {
            var options = {
                types :  [],
                 componentRestrictions: {country: "fr"}

            };
            scope.gPlace = new google.maps.places.Autocomplete(element[0],
                    options);

            google.maps.event.addListener(scope.gPlace, 'place_changed',
                    function() {
                          var geoComponents = scope.gPlace.getPlace();
                            var latitude = geoComponents.geometry.location.lat();
                        var longitude = geoComponents.geometry.location.lng();
                        var addressComponents = geoComponents.address_components;
                        scope.$apply(function() {
                            model.$setViewValue(element.val());
                            console.log(element.val());
                            console.log("Latitude : "  + latitude +"  Longitude : " + longitude);
                        });
                    });
        }

    };

})

thanks for ur help

@degaray
Copy link

degaray commented Mar 14, 2017

I am trying to add this directive into an ionic project and use it for two different fields: from, to. It works great with from, but if I start writing from, then delete it, type it again and select it, 'to' becomes un selectable. Any ideas how I can make it work?

@mukeshshahi
Copy link

mukeshshahi commented Mar 17, 2017

hi please suggest how to select multiple location

@Teaumate
Copy link

Teaumate commented Apr 26, 2017

added google sample code in order to implement proximity autocomplete.

            // Bias the autocomplete object to the user's geographical location,
            // as supplied by the browser's 'navigator.geolocation' object.
            if (navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(function(position) {
                    var geolocation = {
                        lat: position.coords.latitude,
                        lng: position.coords.longitude
                    };
                    var circle = new google.maps.Circle({
                        center: geolocation,
                        radius: position.coords.accuracy
                    });
                    scope.gPlace.setBounds(circle.getBounds());
                });
            }
            scope.gPlace = new google.maps.places.Autocomplete(element[0], options);

@droidthings
Copy link

Hi i am trying to implement this in ionic project, selection from the dropdown works fine on android devices, but on ios devices the dropdown values are un selectable, any ideas how to make it work ? or are there any other directives which i can use in an ionic project ?.

@gpcaretti
Copy link

gpcaretti commented May 31, 2017

This is my improve of the solution from @NateJBeck.

Improvements:

  1. aggreated returned value into a unique json
  2. Enforced the research of zip, city, etc. (see $scope.extract)
  3. Added long. e lat.
  4. added a callback on found place
<my-google-autocomplete id="address" name="address"
			ng-model="vm.formValues.address"
			google-model="vm.formValues.googleAddress"
			on-select="vm.onSelectGoogleAddress($item)"
			autocomplete="off"
			required>
</my-google-autocomplete>

The directive:

angular.module('google-place-autocomplete', [])	// Gp renamed and add []
.directive('myGoogleAutocomplete', function () {
	return {
		replace: true,
		require: 'ngModel',
		scope: {
			ngModel: '=',
			googleModel: '=',
			onSelect: '&?',	// optional callback on selected successfully: 'onPostedBid(googleModel)'
		},
		template: '<input class="form-control" type="text" autocomplete="off">',
		link: function ($scope, element, attrs, model) {
			var googleOptions = {
				types: ['address'],  // change or empty it, if you want no restrictions 
				componentRestrictions: { country: 'it' }  // change or empty it, if you want no restrictions
			};

			var autocomplete = new google.maps.places.Autocomplete(element[0], googleOptions);

			google.maps.event.addListener(autocomplete, 'place_changed', function () {

				/**
				 * Search gor the passed 'type' of info into the google place component
				 * @param {type} components
				 * @param {type} type
				 * @returns {type} 
				 */
				$scope.extract = function (components, type) {
					for (var i = 0; i < components.length; i++)
						for (var j = 0; j < components[i].types.length; j++)
							if (components[i].types[j] == type) return components[i].short_name;
					return '';
				};


				$scope.$apply(function () {
					var place = autocomplete.getPlace();
					if (!place.geometry) {
						// User entered the name of a Place that was not suggested and pressed the Enter key, or the Place Details request failed.
						model.$setValidity('place', false);
						//console.log("No details available for input: '" + place.name + "'");
						return;
					}

					$scope.googleModel = {};
					$scope.googleModel.placeId = place.place_id;
					$scope.googleModel.latitude = place.geometry.location.lat();
					$scope.googleModel.longitude = place.geometry.location.lng();
					$scope.googleModel.formattedAddress = place.formatted_address;
					if (place.address_components) {
						$scope.googleModel.address = [
							$scope.extract(place.address_components, 'route'),
							$scope.extract(place.address_components, 'street_number')
						].join(' ');
						$scope.googleModel.cityName = $scope.extract(place.address_components, 'locality');
						$scope.googleModel.provName = $scope.extract(place.address_components, 'administrative_area_level_2');
						$scope.googleModel.regionName = $scope.extract(place.address_components, 'administrative_area_level_1');
						$scope.googleModel.zipCodeId = $scope.extract(place.address_components, 'postal_code');
						$scope.googleModel.countryCode = $scope.extract(place.address_components, 'country');
					}

					model.$setViewValue(element.val());
					model.$setValidity('place', true);
					if (attrs.onSelect) $scope.onSelect({ $item: $scope.googleModel });
				});
			});
		}
	}
});

@fractalocity
Copy link

I don't think the code in this project is valid any longer.
the following page seems to work and has a plunker example
https://github.com/vskosp/vsGoogleAutocomplete
http://plnkr.co/edit/sdcIaQ?p=preview

@yogesh61990
Copy link

yogesh61990 commented Aug 1, 2017

i am trying to same code but grtting angular.js:14525 Error: [$controller:ctrlreg] error .

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places&key=api-key"></script> <script src="app.js"></script>
<div>Google Places Autocomplte integration in Angular</div>

<div>To Test, start typing the name of any Indian city</div>

<div>selection is: {{chosenPlace}}</div>

<div><input ng-model="chosenPlace" googleplace/></div>

@usman10scorpio
Copy link

usman10scorpio commented Sep 7, 2017

How can I use make this directive useful if i have multiple middle points in ionic ?

This is my code

<label class="item item-input">
          <input type="text" id="location" placeholder="Your Currunt Location" ng-model="location.address" googleplace="" class="ng-pristine ng-valid" autocomplete="on"><!-- googleplace -->
       </label>
     <label class="item item-input">
          <input type="text" id="middle" placeholder="Pick up point" ng-model="middle.address" googleplace="" class="ng-pristine ng-valid" autocomplete="on"><!-- googleplace -->
       </label>
     <label class="item item-input">
          <input type="text" id="end" placeholder="Exit point" ng-model="end.address" googleplace="" class="ng-pristine ng-valid" autocomplete="on"><!-- googleplace -->
       </label>

what if i have multiple middle points and i am making a route between them? How would i do that ?

@Dhaval4241
Copy link

Dhaval4241 commented Oct 2, 2017

when i am using this directive i getting error ReferenceError: google is not defined please give solution for that

@baruchvlz
Copy link

baruchvlz commented Oct 18, 2017

@Dhaval4241 You need to add the Google script to your index.html

<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key={YOUR_API_KEY}&libraries=places&sensor=false"></script>

google should now be avaiable in the window scope.

@bindi11
Copy link

bindi11 commented Mar 30, 2018

Helped me a lot. Thanks!

Can you help me out in binding places for specific 1 city only?

My current directive code is,

angular.module('testApp').directive('googlePlace', function () {
       return {
           require: 'ngModel',
           link: function (scope, element, attrs, model) {

               var southWest = new google.maps.LatLng(36.970298, -87.01993499999998);
               var northEast = new google.maps.LatLng(42.5083379, -91.51307889999998);

               var SyracruiseBounds = new google.maps.LatLngBounds(southWest, northEast);

              var options = {
                   bounds: SyracruiseBounds,
                   types: ['address']
               };
               scope.gPlace = new google.maps.places.Autocomplete(element[0], options);
               scope.gPlace.bindTo('bounds', map);
               scope.gPlace.setOptions({ strictBounds: true });

               google.maps.event.addListener(scope.gPlace, 'place_changed', function () {

                   var geoComponents = scope.gPlace.getPlace();

                   var latitude = geoComponents.geometry.location.lat();
                   var longitude = geoComponents.geometry.location.lng();
                   var addressComponents = geoComponents.address_components;

                   addressComponents = addressComponents.filter(function (component) {
                       switch (component.types[0].locality) {
                           case "Syracruse": // city
                               return true;
                           default:
                               return false;
                       }
                   }).map(function (obj) {
                       return obj.long_name;
                   });

                   addressComponents.push(latitude, longitude);

                   scope.$apply(function () {
                       scope.details = addressComponents; // array containing each location component
                       model.$setViewValue(element.val());
                   });
               });
          }
       };
   });

@sarangpat
Copy link

Hi, this helped me a lot. Thank you for sharing this. Just to get some more information on this, is it possible that using this directive, i can have two places in a form which has this directive defined? I tried attaching this directive to two input elements and it only works for one. Anything on this would be a great help. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment