I am trying to use Google's Places Autocomplete API on a Blazor server-side project but i'm getting the following errors in the console:
InvalidValueError: not an instance of HTMLInputElement
_.hd @ js?key=AIzaSyBqbfM7v…initAutocomplete:61
_.kd @ js?key=AIzaSyBqbfM7v…initAutocomplete:62
zu @ js?key=AIzaSyBqbfM7v…initAutocomplete:20
initAutocomplete @ googleAddressLookup.js:15
(anonymous) @ blazor.server.js:8
beginInvokeJSFromDotNet @ blazor.server.js:8
(anonymous) @ blazor.server.js:1
e.invokeClientMethod @ blazor.server.js:1
e.processIncomingData @ blazor.server.js:1
connection.onreceive @ blazor.server.js:1
i.onmessage @ blazor.server.js:1
I can only assume that this is the blazor.server.js file blocking something somewhere as I have this working within a Razor Page outside of the Blazor environment. My JavaScript knowledge is very limited.
My JS code for Google Autocomplete (I've only made minor changes - it's mostly straight from the Google Dev Docs):
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() {
autocomplete = new google.maps.places.Autocomplete(
document.getElementById('autocomplete'), { types: ['geocode'] });
autocomplete.setFields(['address_component', 'geometry']);
autocomplete.addListener('place_changed', fillInAddress);
}
function fillInAddress() {
var place = autocomplete.getPlace();
document.getElementById('autocomplete').id = 'street_number';
for (var component in componentForm) {
document.getElementById(component).value = '';
document.getElementById(component).disabled = false;
}
document.getElementById("hidden_lat_input").value = place.geometry.location.lat();
document.getElementById("hidden_lng_input").value = place.geometry.location.lng();
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;
}
}
document.getElementById('street_number').id = 'autocomplete';
}
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());
});
}
}
The scripts are in the "_Host.cshtml" file after the blazor.js file:
<script src="_framework/blazor.server.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key={APIKEY}&libraries=places&callback=initAutocomplete" async defer></script>
<script src="~/js/googleAddressLookup.js"></script>
Debugging in the browser shows that initAutocomplete() is being called as intended and I am using the following in my Razor Component to call geolocate(), this is also being called when intended:
private async Task GeoLocate()
{
await Js.InvokeVoidAsync("geolocate");
}
Some of my form (the rest omitted for brevity):
<div id="addressInput" class="@AddressInputsCss">
<input @bind="Advert.Address.Latitude" hidden id="hidden_lat_input" />
<input @bind="Advert.Address.Longitude" hidden id="hidden_lng_input" />
<div class="form-group mb-4">
<input @bind="Advert.Address.Address1" id="autocomplete"
@onfocus="(async () => await GeoLocate())" class="form-control shadow-sm" placeholder="Address *" />
</div>
<div class="form-group mb-4">
<input @bind="Advert.Address.Address2" class="form-control shadow-sm" id="route" placeholder="Address 2 (Optional)" />
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group mb-4">
<input @bind="Advert.Address.City" class="form-control shadow-sm" id="locality" placeholder="City/Town" />
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-4">
<input @bind="Advert.Address.State" class="form-control shadow-sm" id="administrative_area_level_1" placeholder="State" />
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group mb-4">
<input @bind="Advert.Address.Country" class="form-control shadow-sm" id="country" placeholder="Country *" />
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-4">
<input @bind="Advert.Address.PostCode" class="form-control shadow-sm" id="postal_code" placeholder="Postal Code *" />
</div>
</div>
</div>
</div>
The only difference between this and the working version I have on a Razor page is the @bind attributes to the form model instead of asp-for="..." and the need to use JSInterop which I don't think I'm doing correctly, however I can't find a solution elsewhere.