custom - Cómo cargar google maps api de forma asíncrona en Angular2
google maps marker title (4)
Por lo general, en un sitio de Javascript simple, puedo usar el siguiente script para hacer referencia a la API de Google Maps y configurar la función de callback
con initMap
.
<script async defer src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script>
Lo que observé es que la función initMap
en el sitio de javascript sin formato está bajo el alcance de la ventana, y se puede hacer referencia en la configuración de parámetros del script - ?callback=initMap
, pero una vez escribo un componente en angular2 con un método de componente llamado initMap
, el initMap
estará bajo el alcance de mi componente. Entonces, el script de carga asíncrono que configuré en el índice no podrá capturar mi componente initMap
.
Específicamente, ¿me gustaría saber cómo lograr lo mismo en Angular2
?
PD: Sé que hay un
angular2-google-maps
disponible enalpha
través denpm
, pero actualmente se entrega con una capacidad limitada, así que me gustaría saber cómo cargarlo de una manera más fácil sin usar otro componente para poder hacerlo. solo usa google maps api para implementar mi proyecto.
En caso de que quieras convertirla en una función estática, que siempre devuelve una promesa, pero solo obtiene la API una vez.
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;
}
}
Así es como puedes obtener la api en otros scripts:
GoogleMapsLoader.load()
.then((_mapsApi) => {
debugger;
this.geocoder = new _mapsApi.Geocoder();
this.geocoderStatus = _mapsApi.GeocoderStatus;
});
Encontré esto mientras intentaba desarrollar una aplicación web progresiva, es decir, donde existía la posibilidad de no estar en línea. Hubo un error en los ejemplos de código: onload
en el script de google maps debería ser callback
. Así que mi modificación de user2467174 llevó a
map-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;
}
}
Y luego tengo un componente con
import { GoogleMapsLoader } from ''./map/map-loader.service'';
constructor() {
GoogleMapsLoader.load()
.then(res => {
console.log(''GoogleMapsLoader.load.then'', res);
this.mapReady = true;
})
Y una plantilla
<app-map *ngIf=''mapReady''></app-map>
De esta manera, el mapa div solo se pone en el dom si está en línea.
Y luego, en map.component.ts , podemos esperar hasta que el componente se coloque en el DOM antes de cargar el mapa en sí.
ngOnInit() {
if (typeof google !== ''undefined'') {
console.log(''MapComponent.ngOnInit'');
this.loadMap();
}
}
Esto es lo que estoy usando actualmente:
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();
}));
}
});
}
Vea mis instrucciones más completas here
Veo que no quieres otro componente, pero el polímero tiene componentes que funcionan bien con las API de Google. Tengo un código angular2 que utiliza el polímero de youtube datos api. Tuve ayuda para configurarlo. Aquí está el plunker que me hizo empezar. Creo que la parte difícil es prepararse para esa devolución de llamada, estoy seguro de que puedes hacerlo sin polímero. El ejemplo muestra la parte complicada que se usa un servicio angular para conectar todo.
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);
}
}