Как загружать карты Google api асинхронно в Angular2

Обычно на простом javascript-сайте я могу использовать следующий script для ссылки на google maps api и установить callback с помощью initMap.

<script async defer src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script>

То, что я наблюдал, это функция initMap на простом javascript сайте находится под областью окна, и на нее можно ссылаться в параметрах script - ?callback=initMap, но как только я напишу компонент в angular2 с компонентным методом, называемым initMap, initMap будет находиться в рамках моего компонента. Тогда асинхронная загрузка script, которую я установил в индексе, не сможет поймать мой компонент initMap.

В частности, я хотел бы знать, как добиться того же самого в Angular2?

PS: Я знаю, что есть angular2-google-maps компонент alpha через npm, но в настоящее время он поставляется с ограниченными возможностями, поэтому я хотел бы знать, как загрузить его более простым способом, не используя другой компонент, поэтому я могу просто использовать google maps api для реализации моего проекта.

Ответы

Ответ 1

Я вижу, что вам не нужен другой компонент, но у полимера есть компоненты, которые хорошо работают с google apis. У меня есть код angular2, который использует данные apt. Я получил помощь в настройке. Вот plunker, который заставил меня начать. Я думаю, что hardpart получает настройку для этого обратного вызова, я уверен, что вы можете сделать это без полимера. В этом примере показана сложная часть, используемая службой angular для подключения всего.

    const url = 'https://apis.google.com/js/client.js?onload=__onGoogleLoaded'

    export class GoogleAPI {
      loadAPI: Promise<any>
      constructor(){
        this.loadAPI = new Promise((resolve) => {
          window['__onGoogleLoaded'] = (ev) => {
            console.log('gapi loaded')
            resolve(window.gapi);
          }
          this.loadScript()
        });
        
      }
      
      doSomethingGoogley(){
        return this.loadAPI.then((gapi) => {
          console.log(gapi);
        });
      }
      
      loadScript(){
        console.log('loading..')
        let node = document.createElement('script');
        node.src = url;
        node.type = 'text/javascript';
        document.getElementsByTagName('head')[0].appendChild(node);
        
      }
    }

Ответ 2

Я столкнулся с этим, пытаясь разработать прогрессивное веб-приложение, то есть там, где была возможность не быть в сети. В примерах ошибок была ошибка: onload на картах google script должна быть callback. Поэтому моя модификация user2467174 привела к

карта-loader.service.ts

const url = 'http://maps.googleapis.com/maps/api/js?key=xxxxx&callback=__onGoogleLoaded';

@Injectable()
export class GoogleMapsLoader {
    private static promise;

    public static load() {
        // First time 'load' is called?
        if (!GoogleMapsLoader.promise) {

            // Make promise to load
            GoogleMapsLoader.promise = new Promise( resolve => {

                // Set callback for when google maps is loaded.
                window['__onGoogleLoaded'] = (ev) => {
                    resolve('google maps api loaded');
                };

                let node = document.createElement('script');
                node.src = url;
                node.type = 'text/javascript';
                document.getElementsByTagName('head')[0].appendChild(node);
            });
        }

        // Always return promise. When 'load' is called many times, the promise is already resolved.
        return GoogleMapsLoader.promise;
    }
}

И тогда у меня есть компонент с

import { GoogleMapsLoader } from './map/map-loader.service';
constructor() {

    GoogleMapsLoader.load()
    .then(res => {
        console.log('GoogleMapsLoader.load.then', res);
        this.mapReady = true;
    })

И шаблон

<app-map *ngIf='mapReady'></app-map>

Таким образом, карта div помещается только в dom, если он находится в сети.

И затем в map.component.ts мы можем подождать, пока компонент не будет помещен в DOM перед загрузкой самой карты.

ngOnInit() {
    if (typeof google !== 'undefined') {
        console.log('MapComponent.ngOnInit');
        this.loadMap();
    }
}

Ответ 3

На всякий случай вы хотите сделать его статической функцией, которая всегда возвращает обещание, но только получает api один раз.

const url = 'https://maps.googleapis.com/maps/api/js?callback=__onGoogleMapsLoaded&ey=YOUR_API_KEY';

export class GoogleMapsLoader {
    private static promise;
    public static load() {

    // First time 'load' is called?
    if (!GoogleMapsLoader.promise) {

        // Make promise to load
        GoogleMapsLoader.promise = new Promise((resolve) => {

            // Set callback for when google maps is loaded.
            window['__onGoogleMapsLoaded'] = (ev) => {
                console.log('google maps api loaded');
                resolve(window['google']['maps']);
            };

            // Add script tag to load google maps, which then triggers the callback, which resolves the promise with windows.google.maps.
            console.log('loading..');
            let node = document.createElement('script');
            node.src = url;
            node.type = 'text/javascript';
            document.getElementsByTagName('head')[0].appendChild(node);
        });
    }

    // Always return promise. When 'load' is called many times, the promise is already resolved.
    return GoogleMapsLoader.promise;
}

}

Вот как вы можете получить api в других скриптах:

GoogleMapsLoader.load()
            .then((_mapsApi) => {
                debugger;
                this.geocoder       = new _mapsApi.Geocoder();
                this.geocoderStatus = _mapsApi.GeocoderStatus;
            });

Ответ 4

Это то, что я сейчас использую:

loadMapsScript(): Promise<void> {
    return new Promise(resolve => {
      if (document.querySelectorAll(`[src="${mapsScriptUrl}"]`).length) {
        resolve();
      } else {
        document.body.appendChild(Object.assign(document.createElement('script'), {
        type: 'text/javascript',
        src: mapsScriptUrl,
        onload: doMapInitLogic();
      }));
    }
  });
}

См. мои более полные инструкции здесь