首页 > 解决方案 > 如何在 Angular 2 中加载谷歌地图服务

问题描述

如官方文档中所述,我设法延迟加载Angular 的内置 Google Maps 组件

export class GoogleMapsDemoComponent {
    apiLoaded: Observable<boolean>;
    geoCoder: any;

    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)),
        );
     }
    
    ngOnInit(): void {
        this.apiLoaded.subscribe(() => this.initGeoCoder());
    }
    
    initGeoCoder() {
        this.geoCoder = new google.maps.Geocoder();
    }
}

如果组件被多次初始化,我现在会遇到错误。

You have included the Google Maps JavaScript API multiple times on this page. This may cause unexpected errors.

因此,我想将 Google Script 的加载移至专用服务,并将其注入到所有需要它的组件中。

GitHub repo 中有一个讨论证实了这种方法:

您应该能够重构 httpClient.jsonp('https://maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE', 'callback') 和周围的 api 加载到服务中,以便它可以充当一个单例并防止多个负载。请注意,您需要将 providedIn: 'root' 添加到您的 @Injectable 中,这样它就不会为每个模块创建一个实例,从而为每个实例加载 api。

虽然我没有设法正确地做到这一点,但我经常收到以下错误消息,当 Google 脚本尚未加载时似乎会出现:

ERROR ReferenceError: google is not defined

服务:

export class GeoService {
    apiLoaded: Observable<boolean>;

    constructor(private http: HttpClient) {

        this.apiLoaded = this.http
            .jsonp(
                'https://maps.googleapis.com/maps/api/js?key=API_KEY',
                'callback'
            )
            .pipe(
              map(() => true),
              catchError((error) => of(error))
            );
    }
}

零件:

export class GoogleMapsDemoComponent {
    apiLoaded: Observable<boolean>;
    geoCoder: any;

    constructor(geo: GeoService) {}
    
    ngOnInit(): void {
        this.geo.apiLoaded.subscribe(() => this.initGeoCoder());
    }
    
    initGeoCoder() {
        this.geoCoder = new google.maps.Geocoder();
    }
}

我的服务位于一个共享模块中,该模块被导入到组件的模块中。我还尝试在同一个模块中使用服务,以确保我没有搞砸导入,但我收到了相同的错误消息。

谁能告诉我我在这里缺少什么?

标签: angulargoogle-maps

解决方案


我最终结合了将谷歌地图api加载到angular2的最佳方法是什么?https://github.com/angular/components/issues/21665

地理服务.ts:

geocode(
    request: google.maps.GeocoderRequest
 ): Observable<google.maps.GeocoderResult[]> {
    return new Observable<google.maps.GeocoderResult[]>((observer) => {
      if (!this.geocoder) {
        this.geocoder = new google.maps.Geocoder();
      }
      this.geocoder.geocode(request, (results, status) => {
        // need to manually trigger ngZone because "geocode" callback is not picked up properly by Angular
        this.ngZone.run(() => {
          if (status === google.maps.GeocoderStatus.OK) {
            // if status is "OK", the response contains a valid GeocoderResponse.
            observer.next(results);
            observer.complete();
          } else {
            observer.error(status);
          }
        });
      });
    });
  }

  loadGoogleMaps(url: string, id: string, callback): void {
    if (!document.getElementById(id)) {
      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = url;
      script.id = id;
      if (callback) {
        script.addEventListener(
          'load',
          (e) => {
            callback(null, e);
          },
          false
        );
      }
      document.head.appendChild(script);
    }
  }

零件:

ngAfterViewInit() {
  this.geo.loadGoogleMaps(environment.googleMapsURL, 'google-map', () => {
    this.initGeoCoder();
});

geoDataFromAddress(
    address: google.maps.GeocoderRequest['address']
  ): Observable<google.maps.GeocoderResult> {
    return this.geo
      .geocode({ address: address.toLocaleLowerCase() })
      .pipe(map((results) => results[0]));
  }

推荐阅读