Skip to content

Instantly share code, notes, and snippets.

@victorb
Created September 24, 2013 16:37
Show Gist options
  • 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>
@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