Reputation: 326
I'm building a location search input using google's location suggestions API. It all works fine, and the results are returned near-instantly, but I'm having several issues with displaying the data.
I've written a basic copy of what I am trying to do on StackBlitz, https://google-location-suggestions.stackblitz.io, and still having the same issues here. I've updated it to have a loading state which better shows the issue.
StackBlitz link with editor: https://stackblitz.com/edit/google-location-suggestions
I've also included a video here so you can see what I mean: https://vimeo.com/271476965
I'd appreciate anyones help if they know why this is happening.
Upvotes: 4
Views: 1710
Reputation: 7733
The "delay" is related to change detection, if you click the document after entring a location search, angular will get the event and refresh the DOM.
The maps api is downloaded after the angular loading, which makes the maps code running outside the ngZone. I understood this while reading this post.
The main fix (as you mentionned previously on your answer) is to use ngZone.run
to run the code in the Angular Zone :
private suggestions$ = new BehaviorSubject([]);
public search(location: string): void {
this.service.getPlacePredictions(
{
input: location,
componentRestrictions: { country: 'uk' }
},
(predictions, status) => this._ngZone.run(() => this.suggestions$.next(predictions.map(p => p.description)))
);
}
I also added some other improvements :
Loading the maps api only once in the constructor to avoid the already loaded error (also stackblitz aot causes loading the service multiple times, so I change the reload mechanism to page on the left bottom menu) :
constructor(private mapsAPILoader: MapsAPILoader) {
this.mapsAPILoader.load().then(() => {
this.service = new google.maps.places.AutocompleteService();
console.log('Maps API loaded');
this.mapsApiLoading$.next(false);
});
}
You could create the desired observable using rxjs pipes :
this.locationSuggestions$ = this.locationForm.get('location').valueChanges.pipe(
filter(location => location && location !== ''),
switchMap(location =>
this.appService.getLocationSuggestions(location)));
This way you can directly subscribe to your observable in the template using async
:
<ng-container *ngIf="locationSuggestions$ | async as suggestions; else loading">
{{ suggestions | json }}
</ng-container>
<ng-template #loading>
loading...
</ng-template>
Sometimes, if I was fast enough I could search before loading the maps api, so I added a loading observable, to let the component know when he could start searching.
Here is the new stackblitz with the changes.
Upvotes: 4
Reputation: 326
Problem as suggested by @ibenjelloun (thank you) was change detection.
I implemented Angular's NgZone to run change detection so that the binding updates by wrapping the code in the ngZone.run
method
this.ngZone.run(() => {
this.locationSuggestionsLoading = false;
this.locationSuggestions = locations;
});
Upvotes: 1