Home > database >  Angular - Lazy Loading google maps - "You have included the Google Maps JavaScript API multiple
Angular - Lazy Loading google maps - "You have included the Google Maps JavaScript API multiple

Time:11-16

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!

  • Related