var app = angular.module('plunker', ['vsGoogleAutocomplete']);

app.controller('MainCtrl', function($scope) {
  
  $scope.streetNumber = {
    name: '',
    place: '',
    components: {
      placeId: '',
      streetNumber: '', 
      street: '',
      city: '',
      state: '',
      countryCode: '',
      country: '',
      postCode: '',
      district: '',
      location: {
        lat: '',
        long: ''
      }
    }
  };
  
});
<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>vsHandyStorage Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link data-require="bootstrap-css" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
    
    <!-- Google Maps JavaScript API -->
    <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places"></script>
    
    <!-- vsGoogleAutocomplete -->
    <script src="vs-google-autocomplete.js"></script>
    
    <!-- validator for autocomplete -->
    <script src="vs-autocomplete-validator.js"></script>
    
    <script src="app.js"></script>
  </head>


  <!-- 
  * Dependency: Google Maps JavaScript API v3
  * Google Maps API info: https://developers.google.com/maps/documentation/javascript/places-autocomplete
  * Simple usage: 
  *   1. add <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places"></script>
  *   2. add vs-google-autocomplete.js
  *   3. add vs-google-autocomplete directive to input field
  * Validator usage:
  *   1. add vs-autocomplete-validator.js
  *   2. add vs-autocomplete-validator directive to input field
  -->
  <body ng-controller="MainCtrl">
    <div class="container">
      
      <h3>Example of vsGoogleAutocomplete module <u>with validator</u></h3>
      <h5>Also look at <a href="http://plnkr.co/edit/sdcIaQ?p=preview">simple usage</a> of this module without embedded validator</h5>
      
      <form name="addrForm">

        <!-- 
        * Example: vs-autocomplete-validator
        * Info: checks if place is valid Google address (default config)
        -->
        <div ng-class="{'form-group':true, 'has-error':addrForm.place.$dirty && addrForm.place.$invalid}">
          <label for="place" class="control-label">Place</label>
          <input vs-google-autocomplete
                 vs-autocomplete-validator
                 
                 ng-model="place" 
                 type="text"
                 name="place"
                 id="place"
                 class="form-control" 
                 placeholder="Enter any address">
          <span class="help-block"><b><u>Valid</u>: </b>{{addrForm.place.$valid}}</span>
          <span class="help-block"><b><u>NgModelController.$error</u>: </b>{{addrForm.place.$error}}</span>
          <span class="help-block"><b><u>NgModelController.vsAutocompleteErorr</u>: </b>{{addrForm.place.vsAutocompleteErorr}}</span>
          <span class="help-block"><b>Model: </b>{{place}}</span>
        </div>
        
        <!-- 
        * Example: vs-autocomplete-validator="vs-street-address"
        * Info: checks if place is full street address (street number, street, ...)
        -->
        <div ng-class="{'form-group':true, 'has-error':addrForm.streetNumber.$dirty && addrForm.streetNumber.$invalid}">
          <label for="streetNumber" class="control-label">Street number</label>
          <input vs-google-autocomplete="{ types: ['address'] }"
                 vs-autocomplete-validator="vs-street-address"
          
                 ng-model="streetNumber.name"
                 vs-place="streetNumber.place" 
                 vs-place-id="streetNumber.components.placeId"
                 vs-street-number="streetNumber.components.streetNumber" 
                 vs-street="streetNumber.components.street"
                 vs-city="streetNumber.components.city"
                 vs-state="streetNumber.components.state"
                 vs-country-short="streetNumber.components.countryCode"
                 vs-country="streetNumber.components.country"
                 vs-post-code="address.components.postCode"
                 vs-district="address.components.district"
                 vs-latitude="streetNumber.components.location.lat"
                 vs-longitude="streetNumber.components.location.long"
                 
                 type="text" 
                 name="streetNumber"
                 id="streetNumber"
                 class="form-control" 
                 placeholder="Enter full address (street number, street, ...)">
          
          <!-- validation info -->
          <span class="help-block"><b><u>Valid</u>: </b>{{addrForm.streetNumber.$valid}}</span>
          <span class="help-block"><b><u>NgModelController.$error</u>: </b>{{addrForm.streetNumber.$error}}</span>
          <span class="help-block"><b><u>NgModelController.vsAutocompleteErorr</u>: </b>{{addrForm.streetNumber.vsAutocompleteErorr}}</span>
          
          <!-- model -->
          <span class="help-block"><b>Model: </b>{{streetNumber.name}}</span>
          <span class="help-block"><b>Place id: </b>{{streetNumber.components.placeId}}</span>
          <span class="help-block"><b>Street number: </b>{{streetNumber.components.streetNumber}}</span>
          <span class="help-block"><b>Street: </b>{{streetNumber.components.street}}</span>
          <span class="help-block"><b>City: </b>{{streetNumber.components.city}}</span>
          <span class="help-block"><b>State: </b>{{streetNumber.components.state}}</span>
          <span class="help-block"><b>Country code: </b>{{streetNumber.components.countryCode}}</span>
          <span class="help-block"><b>Country: </b>{{streetNumber.components.country}}</span>
          <span class="help-block"><b>Postcode: </b>{{address.components.postCode}}</span>
          <span class="help-block"><b>District: </b>{{address.components.district}}</span>
          <span class="help-block"><b>Latitude: </b>{{streetNumber.components.location.lat}}</span>
          <span class="help-block"><b>Longitude: </b>{{streetNumber.components.location.long}}</span>
          
          <pre class="help-block"><b>Place: </b>{{streetNumber.place | json}}</pre>
        </div>
        
      </form>

    </div>
  </body>

</html>
/* Put your css in here */

/**
 * vsGoogleAutocomplete - v0.5.0 - 2015-11-29
 * https://github.com/vskosp/vsGoogleAutocomplete
 * Copyright (c) 2015 K.Polishchuk
 * License: MIT
 */
(function (window, document) {
    'use strict';
    angular.module('vsGoogleAutocomplete', []);
    
    angular.module('vsGoogleAutocomplete').service('vsGooglePlaceUtility', function() {
        function isGooglePlace(place) {
          if (!place)
        		return false;
        	return !!place.place_id;
        }
     
        function isContainTypes(place, types) {
          var placeTypes,
        	    placeType,
        	    type;	
        	if (!isGooglePlace(place))
        	  return false;
        	placeTypes = place.types;
        	for (var i = 0; i < types.length; i++) {
        	  type = types[i];
            for (var j = 0; j < placeTypes.length; j++) {
        	    placeType = placeTypes[j];
              if (placeType === type) {
          	    return true;
          	}
          }
      	}
      	return false;
      }
    	
    	function getAddrComponent(place, componentTemplate) {
        var result;
        if (!isGooglePlace(place))
          return;
        for (var i = 0; i < place.address_components.length; i++) {
          var addressType = place.address_components[i].types[0];
          if (componentTemplate[addressType]) {
            result = place.address_components[i][componentTemplate[addressType]];
            return result;
          }
        }
        return;
      }
    	
    	  function getPlaceId(place) {
          if (!isGooglePlace(place))
        	    return;
        	return place.place_id;
        }
    	
        function getStreetNumber(place) {
          var COMPONENT_TEMPLATE = { street_number: 'short_name' },
        	    streetNumber = getAddrComponent(place, COMPONENT_TEMPLATE);
    		  return streetNumber;
        }
    	
        function getStreet(place) {
          var COMPONENT_TEMPLATE = { route: 'long_name' },
        	    street = getAddrComponent(place, COMPONENT_TEMPLATE);
    		  return street;
        }
    	
        function getCity(place) {
          var COMPONENT_TEMPLATE = { locality: 'long_name' },
        	    city = getAddrComponent(place, COMPONENT_TEMPLATE);
    		  return city;
        }
    	
        function getState(place) {
          var COMPONENT_TEMPLATE = { administrative_area_level_1: 'short_name' },
        	    state = getAddrComponent(place, COMPONENT_TEMPLATE);
        	return state;
        }
        
        function getDistrict(place) {
          var COMPONENT_TEMPLATE = { administrative_area_level_2: 'short_name' },
      	      state = getAddrComponent(place, COMPONENT_TEMPLATE);
        	return state;
        }
    	
        function getCountryShort(place) {
          var COMPONENT_TEMPLATE = { country: 'short_name' },
        	    countryShort = getAddrComponent(place, COMPONENT_TEMPLATE);
        	return countryShort;
        }
    	
        function getCountry(place) {
        var COMPONENT_TEMPLATE = { country: 'long_name' },
            country = getAddrComponent(place, COMPONENT_TEMPLATE);
      	return country;
      }
      
      function getPostCode(place) {
        var COMPONENT_TEMPLATE = { postal_code: 'long_name' },
            postCode = getAddrComponent(place, COMPONENT_TEMPLATE);
        return postCode;
      }
      
      function isGeometryExist(place) {
        return angular.isObject(place) && angular.isObject(place.geometry);
      }
      
      function getLatitude(place) {
        if (!isGeometryExist(place)) return;
        return place.geometry.location.lat();
      }
      
      function getLongitude(place) {
        if (!isGeometryExist(place)) return;
        return place.geometry.location.lng();
      }
    	
    	return {
    	  isGooglePlace: isGooglePlace,
        isContainTypes: isContainTypes,
        getPlaceId: getPlaceId,
        getStreetNumber: getStreetNumber,
        getStreet: getStreet,
        getCity: getCity,
        getState: getState,
        getCountryShort: getCountryShort,
        getCountry: getCountry,
        getLatitude: getLatitude,
        getLongitude: getLongitude,
        getPostCode: getPostCode,
        getDistrict: getDistrict
    	};
    });
    
    angular.module('vsGoogleAutocomplete').directive('vsGoogleAutocomplete', ['vsGooglePlaceUtility', '$timeout', function(vsGooglePlaceUtility, $timeout) {
      return {
        restrict: 'A',
        require: ['vsGoogleAutocomplete', 'ngModel'],
    	 	scope: {
     			vsGoogleAutocomplete: '=',
          vsPlace: '=?',
    			vsPlaceId: '=?',
    			vsStreetNumber: '=?',
    			vsStreet: '=?',
    			vsCity: '=?',
    			vsState: '=?',
    			vsCountryShort: '=?',
    			vsCountry: '=?',
    			vsPostCode: '=?',
    			vsLatitude: '=?',
    			vsLongitude: '=?',
    			vsDistrict: '=?'
        },
    		controller: ['$scope', '$attrs', function($scope, $attrs) {
    			this.isolatedScope = $scope;
    			
    			/**
          * Updates address components associated with scope model.
          * @param {google.maps.places.PlaceResult} place PlaceResult object
          */
    			this.updatePlaceComponents = function(place) {
    			  $scope.vsPlaceId      = !!$attrs.vsPlaceId  && place     ? vsGooglePlaceUtility.getPlaceId(place)      : undefined;
    				$scope.vsStreetNumber = !!$attrs.vsStreetNumber && place ? vsGooglePlaceUtility.getStreetNumber(place) : undefined;
    				$scope.vsStreet       = !!$attrs.vsStreet && place       ? vsGooglePlaceUtility.getStreet(place)       : undefined;
    				$scope.vsCity         = !!$attrs.vsCity && place         ? vsGooglePlaceUtility.getCity(place)         : undefined;
    				$scope.vsPostCode     = !!$attrs.vsPostCode && place     ? vsGooglePlaceUtility.getPostCode(place)     : undefined;
    				$scope.vsState        = !!$attrs.vsState && place        ? vsGooglePlaceUtility.getState(place)        : undefined;
    				$scope.vsCountryShort = !!$attrs.vsCountryShort && place ? vsGooglePlaceUtility.getCountryShort(place) : undefined;
    				$scope.vsCountry      = !!$attrs.vsCountry && place      ? vsGooglePlaceUtility.getCountry(place)      : undefined;
    				$scope.vsLatitude     = !!$attrs.vsLatitude && place     ? vsGooglePlaceUtility.getLatitude(place)     : undefined;
    				$scope.vsLongitude    = !!$attrs.vsLongitude && place    ? vsGooglePlaceUtility.getLongitude(place)    : undefined;
    				$scope.vsDistrict     = !!$attrs.vsDistrict && place     ? vsGooglePlaceUtility.getDistrict(place)     : undefined;
    			};
    		}],
        link: function(scope, element, attrs, ctrls) {
    			// controllers
    			var autocompleteCtrl = ctrls[0],
    			    modelCtrl = ctrls[1];
    			
    			// google.maps.places.Autocomplete instance (support google.maps.places.AutocompleteOptions)
    			var autocompleteOptions = scope.vsGoogleAutocomplete || {},
    			    autocomplete = new google.maps.places.Autocomplete(element[0], autocompleteOptions);
    			
    			// google place object
    			var place;
    			
    			// value for updating view
    			var	viewValue;
    				
    			// updates view value and address components on place_changed google api event
    			google.maps.event.addListener(autocomplete, 'place_changed', function() {
    				place = autocomplete.getPlace();
    				viewValue = place.formatted_address || modelCtrl.$viewValue;
    				scope.$apply(function() {
    			    scope.vsPlace = place;
    					autocompleteCtrl.updatePlaceComponents(place);
    					modelCtrl.$setViewValue(viewValue);
              modelCtrl.$render();					    
    				});
          });
    			
    			// updates view value on focusout
    			element.on('blur', function(event) {
            viewValue = (place && place.formatted_address) ? viewValue : modelCtrl.$viewValue;
				    $timeout(function() {
				      scope.$apply(function() {
				    	  modelCtrl.$setViewValue(viewValue);
                modelCtrl.$render();
				      });
				    });
          });
    			
    			// prevent submitting form on enter
    			google.maps.event.addDomListener(element[0], 'keydown', function(e) { 
            if (e.keyCode == 13) { 
              e.preventDefault(); 
            }
          });
        } 
      };
    }]);
})(window, document);
/**
 * vsGoogleAutocomplete - v0.5.0 - 2015-11-29
 * https://github.com/vskosp/vsGoogleAutocomplete
 * Copyright (c) 2015 K.Polishchuk
 * License: MIT
 */
(function (window, document) {
    'use strict';
    angular.module('vsGoogleAutocomplete').factory('vsEmbeddedValidatorsInjector', ['$injector', function($injector) {
    	var validatorsHash = [];
    	
    	/**
      * Class making embedded validator.
    	* @constructor
      * @param {string} name - validator name.
      * @param {function(place)} validateMethod - function that will validate place.
      */
    	function EmbeddedValidator(name, validateMethod) {    
    		this.name = name;
    		this.validate = validateMethod;
    	}
    		
    	function searchValidator(validatorName) {
    	  for (var i = 0; i < validatorsHash.length; i++) {
    	    if(validatorsHash[i].name === validatorName)
    		    return validatorsHash[i];
    		}
    		return;
    	}
    	
    	function getValidator(validatorName) {
    	  var validator = searchValidator(validatorName);
    		if(!validator) {
    	    validator = new EmbeddedValidator(validatorName, $injector.get(validatorName));
    			validatorsHash.push(validator);
    		}				
    		return validator;
    	}
    	
    	function getValidators(validatorsNamesList) {
    	  var validatorsList = [];
    		for (var i = 0; i < validatorsNamesList.length; i++) {
    	    var validator = getValidator(validatorsNamesList[i]);
    			validatorsList.push(validator);
    		}
    		return validatorsList;
    	}
    	
    	return {
    	    get: getValidators
    	};
    }]);
    
    angular.module('vsGoogleAutocomplete').service('vsValidatorFactory', ['vsEmbeddedValidatorsInjector', function(vsEmbeddedValidatorsInjector) {
    	/**
      * Class making validator associated with vsGoogleAutocomplete controller.
    	* @constructor
      * @param {Array.<string>} validatorsNamesList - List of embedded validator names.
      */
    	function Validator(validatorsNamesList) {
        // add default embedded validator name
    		validatorsNamesList.unshift('vsGooglePlace');
    		
        this._embeddedValidators = vsEmbeddedValidatorsInjector.get(validatorsNamesList);
    		this.error = {};
    		this.valid = true;
    	}
    	
    	/**
      * Runs all embedded validators and change the validity state.
    	* @param {google.maps.places.PlaceResult} place - PlaceResult object.
      */
    	Validator.prototype.validate = function(place) {    			
    		var validationErrorKey, isValid;
    		
    		for (var i = 0; i < this._embeddedValidators.length; i++) {
          validationErrorKey = this._embeddedValidators[i].name;
                
    			// runs embedded validator only if place is object
    			if (angular.isObject(place)) {
    		    isValid = this._embeddedValidators[i].validate(place);
    			} else {
            isValid = false;
    			}
    			this._setValidity(validationErrorKey, isValid);
    		}		
    	};
    	
    	/**
      * Sets validity.
    	* @param {string} validationErrorKey - Error name.
    	* @param {boolean} isValid - Valid status.
      */
    	Validator.prototype._setValidity = function(validationErrorKey, isValid) {	
    		// set error
    		if (typeof isValid != 'boolean') {
          delete this.error[validationErrorKey];
        } else {
          if (!isValid) {
            this.error[validationErrorKey] = true;
          } else {
            delete this.error[validationErrorKey];
          }
        }
    		// set validity
    		if (this.error) {
          for (var e in this.error) {
    		    this.valid = false;
            return;
          }
        }
        this.valid = true;
      };
    	
    	this.create = function(validatorsNamesList) {
    		return new Validator(validatorsNamesList);
    	};
    }]);
    
    angular.module('vsGoogleAutocomplete').directive('vsAutocompleteValidator', ['vsValidatorFactory', function(vsValidatorFactory) {
    	/**
      * Parse validator names from attribute.
      * @param {$compile.directive.Attributes} attrs Element attributes
      * @return {Array.<string>} Returns array of normalized validator names.
      */
    	function parseValidatorNames(attrs) {
    	  var attrValue = attrs.vsAutocompleteValidator,
    	      validatorNames = (attrValue!=="") ? attrValue.trim().split(',') : [];
    		
    		// normalize validator names
    		for (var i = 0; i < validatorNames.length; i++) {
    	    validatorNames[i] = attrs.$normalize(validatorNames[i]);
    		}
    		
    		return validatorNames;
    	}
    	
    	return {
    	  restrict: 'A',
    		require: ['ngModel', 'vsGoogleAutocomplete'],
    		link: function(scope, element, attrs, controllers) {
    		  // controllers
        	var modelCtrl = controllers[0],
    		      autocompleteCtrl = controllers[1];
    		    
    			// validator
    			var	validatorNames = parseValidatorNames(attrs),
    		  		validator = vsValidatorFactory.create(validatorNames);
    			
    			// add validator for ngModel
    			modelCtrl.$validators.vsAutocompleteValidator = function() {
    		    return validator.valid;
    		  };
    						
        	// watch for updating place
    			autocompleteCtrl.isolatedScope.$watch('vsPlace', function(place) {
    		  	// validate place
    		  	validator.validate(place);
    				
    				// set addr components to undefined if place is invalid
    				if (!validator.valid) {
    				  autocompleteCtrl.updatePlaceComponents(undefined);
    				}
    				
    				// call modelCtrl.$validators.vsAutocompleteValidator
    				modelCtrl.$validate();
    		  });
    			
    			// publish autocomplete errors
    			modelCtrl.vsAutocompleteErorr = validator.error;
    		}
    	};
    }]);
    
    
    //Validator - checks if place is valid Google address
    angular.module('vsGoogleAutocomplete').factory('vsGooglePlace', ['vsGooglePlaceUtility', function(vsGooglePlaceUtility) {
      function validate(place) {
    		return vsGooglePlaceUtility.isGooglePlace(place);
    	}
    	
    	return validate;
    }]);
    
    //Validator - checks if place is full street address (street number, street, ...)
    angular.module('vsGoogleAutocomplete').factory('vsStreetAddress', ['vsGooglePlaceUtility', function(vsGooglePlaceUtility) {
      var PLACE_TYPES = ["street_address", "premise"];	
    
    	function validate(place) {
    		return vsGooglePlaceUtility.isContainTypes(place, PLACE_TYPES);
    	}
    	
    	return validate;
    }]);
})(window, document);