Как загружать карты 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();
}));
}
});
}
См. мои более полные инструкции здесь