Problem -Google maps autocomplete drop down isn't working on my website main page after parsing and loading the google maps API script. I'm experiencing what I believe to be a race condition on my websites main page, in an input field inside a navbar at the top, where I'm using the google maps autocomplete feature in a custom autocomplete directive. The problem, which is the autocomplete drop down isn't working is occurring in mobile only, desktop seems to be ok. I'm using '<script async defer...' to prevent waiting for the script to finish DL'ing to prase the page, so I believe the directive (containing the places autocomplete) on the input tag is parsed and rendered and being instantiated before the script has time to finish loading. Result, the dropdown doesn't work on the main page (mobile only), other pages are fine.
Solution 1 - Remove 'async defer' and load synchronously, which solves the problem and seems to load pretty fast, but not my ideal solution especially for my websites main page. Maybe someone can chime in here with their thoughts on sync loading maps API before parsing the page?
Solution 2 - Load the Google API script asynchronously and wait for it to finish before instantiating the places autocomplete object and listener inside the directive. I'd like to use the built in callback feature but unsure if this will work, but it seems like an avenue I should try...
Here is my attempt but I need help as I have errors.
Index.html - pseudo code
<html lang="en">
<head>
// other tags left out for brevity
<script src="./index.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=secret-key&libraries=places&callback=initMap" async defer></script>
</head>
<body>
<app-root></app-root>
</body>
</html>
Mainpage.html - pseudo code created for brevity
<nav class="navbar navbar-light bg-light fixed-top">
<form class="form-inline">
<input appGooglePlaces class="form-control google-place-input" placeholder="Search" aria-label="Search" (onSelect)="fetchResults($event)">
</form>
</nav>
Index.ts - I created this file to receive the callback 'initMap' from the script, but not being called and receiving an error saying.
localhost/:1 Uncaught (in promise) fe {message: 'initMap is not a function', name: 'InvalidValueError', stack: 'Error\n at new fe (https://maps.googleapis.com/m…GUFa3DM&libraries=places&callback=initMap:197:125'}
function initMap(): void {
// not being hit
console.log('callback hit');
// instantiate the objects inside the 'appGooglePlaces' directive so we can use it
}
export {
initMap
};
FYI - I can embed the callback function right inside the index.html file inside a script tag, but then I can't call other Angular components, services, directives, etc. Is this possible in Angular?
appGooglePlaces - directive
import {
Directive,
ElementRef,
OnInit,
Output,
EventEmitter,
Input
} from '@angular/core';
import {
Address
} from '../shared/models/address';
@Directive({
selector: '[appGooglePlaces]'
})
export class GooglePlacesDirective implements OnInit {
private element: HTMLInputElement;
private autocomplete: google.maps.places.Autocomplete;
@Output() onSelect: EventEmitter < any > = new EventEmitter();
@Input() countrySelected = '';
constructor(private elRef: ElementRef) {
this.element = elRef.nativeElement;
}
ngOnInit() {
this.autocomplete = new google.maps.places.Autocomplete(this.element);
google.maps.event.addListener(this.autocomplete, 'place_changed', () => {
const place = this.autocomplete.getPlace();
if (place.name === '') {
return false;
}
const address = new Address();
// address processing left out for brevity
this.onSelect.emit(address);
});
}
}