I am trying to use @angular/google-maps.
All is working, but I keep getting the following error on the browser:
"You have included the Google Maps JavaScript API multiple times on this page. This may cause unexpected errors."
I am trying to Lazy Loading the API.
"@angular/animations": "~13.3.0",
"@angular/common": "~13.3.0",
"@angular/compiler": "~13.3.0",
"@angular/core": "~13.3.0",
"@angular/forms": "~13.3.0",
"@angular/google-maps": "^13.3.9",
I have a shared map component, that is used by other components.
So in this component, I tried what the documentation says (documentation):
export class MapComponent {
apiLoaded: Observable<boolean>;
constructor(httpClient: HttpClient) {
this.apiLoaded = httpClient.jsonp('https://maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE', 'callback')
.pipe(
map(() => true),
catchError(() => of(false)),
);
}
...
}
this approach is causing the error. Each time this component is used the constructor is called and well the API is loaded again.
So I tried to do this on a singleton service, like this:
@Injectable({
providedIn: 'root'
})
export class MapServiceService {
apiLoaded!: Observable<boolean>;
constructor(httpClient: HttpClient) {
this.apiLoaded = httpClient.jsonp('https://maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE', 'callback')
.pipe(
map(() => true),
catchError(() => of(false)),
);
}
isApiLoaded(): Observable<boolean> {
return this.apiLoaded;
}
}
And then on the component I am doing this:
export class MapComponent implements OnInit {
options!: google.maps.MapOptions;
apiLoaded!: Observable<boolean>;
constructor(private _mapService: MapServiceService) {
}
ngOnInit(): void {
this.apiLoaded = this._mapService.isApiLoaded();
}
...
I can see that now the service's constructor is called once, but I am still getting that error.
What would be the correct approach for this?
Thanks for reading!
CodePudding user response:
The issue is with calling apiLoaded which is observable.
In the UI I was doing the following:
<div *ngIf="apiLoaded | async">
<google-map height="500px" width="100%" [options]="options"></google-map>
</div>
Basically, I am subscribing to the isApiLoaded() method that ends up trying to load the API each time I render the component.
This is my solution:
export class MapServiceService {
private currentApiStatus: BehaviorSubject<Boolean>;
obsCurrentApiStatus: Observable<Boolean>;
constructor(httpClient: HttpClient) {
this.currentApiStatus = new BehaviorSubject(new Boolean(false));
this.obsCurrentApiStatus = this.currentApiStatus.asObservable();
httpClient.jsonp('https://maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE', 'callback')
.pipe(
map(() => true),
catchError(() => of(false)),
).subscribe( loaded => {
this.currentApiStatus.next(loaded);
});
}
}
then on the component:
apiLoaded!: boolean;
constructor(private _mapService: MapServiceService) {
}
ngOnInit(): void {
this._mapService.obsCurrentApiStatus.subscribe(status => {
this.apiLoaded = status.valueOf();
});
...
and in the UI:
<div *ngIf="apiLoaded">
<google-map height="500px" width="100%" [options]="options"></google-map>
</div>
I hope this saves time for somebody and if you know a better way to implement this I am open to learning!
Thanks!