Usman Iqbal
Usman Iqbal

Reputation: 2429

Angularjs - Why I am getting this InvalidValue error while using Autocomplete address form of Google

I am implementing Google Autocomplete address form for the first time on localhost I have successfully created the APIKey and in credentials I checked HTTP referrers (web sites) and add my localhost url http://localhost:8383.

I am following this Answer plus officialDocumentation.

Now my controller looks something like this

categoriesControllers.controller('PartialsController', ['$scope', '$http', '$routeParams', '$location', function ($scope, $http, $routeParams, $location) {
 var placeSearch, autocomplete;
  var componentForm = {
    street_number: 'short_name',
    route: 'long_name',
    locality: 'long_name',
    administrative_area_level_1: 'short_name',
    country: 'long_name',
    postal_code: 'short_name'
  };

  function initAutocomplete() {
    // Create the autocomplete object, restricting the search to geographical
    // location types.
    autocomplete = new google.maps.places.Autocomplete(
        /** @type {!HTMLInputElement} */(document.getElementById('autocomplete')),
        {types: ['geocode']});

    // When the user selects an address from the dropdown, populate the address
    // fields in the form.
    autocomplete.addListener('place_changed', fillInAddress);
  }

  function fillInAddress() {
    // Get the place details from the autocomplete object.
    var place = autocomplete.getPlace();

    for (var component in componentForm) {
      document.getElementById(component).value = '';
      document.getElementById(component).disabled = false;
    }

    // Get each component of the address from the place details
    // and fill the corresponding field on the form.
    for (var i = 0; i < place.address_components.length; i++) {
      var addressType = place.address_components[i].types[0];
      if (componentForm[addressType]) {
        var val = place.address_components[i][componentForm[addressType]];
        document.getElementById(addressType).value = val;
      }
    }
  }

  // Bias the autocomplete object to the user's geographical location,
  // as supplied by the browser's 'navigator.geolocation' object.
  function geolocate() {
    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
        });
        autocomplete.setBounds(circle.getBounds());
      });
    }
  };}]);

and my html file looks like this

    <div ng-controller="PartialsController">
<div id="locationField">
  <input id="autocomplete" placeholder="Enter your address"
         onFocus="geolocate()" type="text"></input>
</div>

<table id="address">
  <tr>
    <td class="label">Street address</td>
    <td class="slimField"><input class="field" id="street_number"
          disabled="true"></input></td>
    <td class="wideField" colspan="2"><input class="field" id="route"
          disabled="true"></input></td>
  </tr>
  <tr>
    <td class="label">City</td>
    <td class="wideField" colspan="3"><input class="field" id="locality"
          disabled="true"></input></td>
  </tr>
  <tr>
    <td class="label">State</td>
    <td class="slimField"><input class="field"
          id="administrative_area_level_1" disabled="true"></input></td>
    <td class="label">Zip code</td>
    <td class="wideField"><input class="field" id="postal_code"
          disabled="true"></input></td>
  </tr>
  <tr>
    <td class="label">Country</td>
    <td class="wideField" colspan="3"><input class="field"
          id="country" disabled="true"></input></td>
  </tr>
</table>
</div>

I have added the key in in index.html like this

 <script src="https://maps.googleapis.com/maps/api/js?key=xxx&libraries=places&callback=initAutocomplete"
    async defer></script>

Upvotes: 0

Views: 291

Answers (1)

Vadim Gremyachev
Vadim Gremyachev

Reputation: 59368

If you are getting this error:

InvalidValueError: initAutocomplete is not a function

then apparently it occurs since initAutocomplete could not be found. In your case initAutocomplete is a private function defined in the scope of categoriesControllers and its not accessible with callback parameter.

Solution

One option would be to change the way how initAutocomplete function is getting initialized and invoked:

1) Load Google Maps API synchronously and omit callback parameter from query string:

<script src="https://maps.googleapis.com/maps/api/js?key=KEY&libraries=places></script>

2) invoke initAutocomplete function once the controller is instantiated, for example:

$scope.initAutocomplete = function () {
   //...   
}
$scope.initAutocomplete(); 

Example

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

app.controller('myController', function ($scope) {

  $scope.components = {
    street_number: 'short_name',
    route: 'long_name',
    locality: 'long_name',
    administrative_area_level_1: 'short_name',
    country: 'long_name',
    postal_code: 'short_name'
  };

  $scope.formData = {};


  $scope.initAutocomplete = function () {
    var autocomplete = new google.maps.places.Autocomplete(document.getElementById('autocomplete'), {
      types: ['geocode']
    });
    google.maps.event.addListener(autocomplete, 'place_changed', function () {
      $scope.$apply(function(){
          var place = autocomplete.getPlace();
          $scope.fillInAddress(place); 
      });
    });

  }


  $scope.fillInAddress = function (place) {

    Object.keys($scope.components).forEach(function (component) {
      $scope.formData[component] = '';
      document.getElementById(component).disabled = false;
    });

    place.address_components.forEach(function (component) {
      var addressType = component.types[0];
      if ($scope.components[addressType]) {
        $scope.formData[addressType] = component[$scope.components[addressType]];
      }
    });
  }

  $scope.initAutocomplete();
  //google.maps.event.addDomListener(window, 'load', $scope.initAutocomplete);


});
#locationField,
#controls {
  position: relative;
  width: 480px;
}
#autocomplete {
  position: absolute;
  top: 0px;
  left: 0px;
  width: 99%;
}
.label {
  text-align: right;
  font-weight: bold;
  width: 100px;
  color: #303030;
}
#address {
  border: 1px solid #000090;
  background-color: #f0f0ff;
  width: 480px;
  padding-right: 2px;
}
#address td {
  font-size: 10pt;
}
.field {
  width: 99%;
}
.slimField {
  width: 80px;
}
.wideField {
  width: 200px;
}
#locationField {
  height: 20px;
  margin-bottom: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?libraries=places"></script>


<div ng-app='app' ng-controller='myController'>
  <div id="locationField">
    <input id="autocomplete" placeholder="Enter your address" type="text">
  </div>
  <table id="address">
    <tr>
      <td class="label">Street address</td>
      <td class="slimField">
        <input ng-model="formData.street_number" class="field" id="street_number" disabled="true" type="text">
      </td>
      <td class="wideField" colspan="2">
        <input ng-model="formData.route" class="field" id="route" disabled="true">
      </td>
    </tr>
    <tr>
      <td class="label">City</td>
      <td class="wideField" colspan="3">
        <input ng-model="formData.locality" class="field" id="locality" disabled="true">
      </td>
    </tr>
    <tr>
      <td class="label">State</td>
      <td class="slimField">
        <input ng-model="formData.administrative_area_level_1" class="field" id="administrative_area_level_1" disabled="true">
      </td>
      <td class="label">Zip code</td>
      <td class="wideField">
        <input ng-model="formData.postal_code" class="field" id="postal_code" disabled="true">
      </td>
    </tr>
    <tr>
      <td class="label">Country</td>
      <td class="wideField" colspan="3">
        <input ng-model="formData.country" class="field" id="country" disabled="true">
      </td>
    </tr>
  </table>
</div>

Upvotes: 1

Related Questions