diff --git a/src/dev-app/google-map/google-map-demo.html b/src/dev-app/google-map/google-map-demo.html index 60440a485e98..9bb8317d2ab6 100644 --- a/src/dev-app/google-map/google-map-demo.html +++ b/src/dev-app/google-map/google-map-demo.html @@ -1,4 +1,5 @@ -
+
Loading Google Maps API...
+
| null = null; + /** Demo Component for @angular/google-maps/map */ @Component({ selector: 'google-map-demo', @@ -96,6 +98,7 @@ export class GoogleMapDemo { }; isGroundOverlayDisplayed = false; + hasLoaded: boolean; groundOverlayImages = [ { title: 'Red logo', @@ -116,19 +119,16 @@ export class GoogleMapDemo { isBicyclingLayerDisplayed = false; mapTypeId: google.maps.MapTypeId; - mapTypeIds = [ - google.maps.MapTypeId.HYBRID, - google.maps.MapTypeId.ROADMAP, - google.maps.MapTypeId.SATELLITE, - google.maps.MapTypeId.TERRAIN, - ]; + mapTypeIds = ['hybrid', 'roadmap', 'sattelite', 'terrain'] as google.maps.MapTypeId[]; markerClustererImagePath = 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'; directionsResult?: google.maps.DirectionsResult; - constructor(private readonly _mapDirectionsService: MapDirectionsService) {} + constructor(private readonly _mapDirectionsService: MapDirectionsService) { + this._loadApi(); + } authFailure() { console.log('Auth failure event emitted'); @@ -257,4 +257,41 @@ export class GoogleMapDemo { return result; } + + private _loadApi() { + this.hasLoaded = !!window.google?.maps; + + if (this.hasLoaded) { + return; + } + + if (!apiLoadingPromise) { + // Key can be set through the `GOOGLE_MAPS_KEY` environment variable. + const apiKey: string | undefined = (window as any).GOOGLE_MAPS_KEY; + + apiLoadingPromise = Promise.all([ + this._loadScript( + `https://maps.googleapis.com/maps/api/js?libraries=visualization${ + apiKey ? `&key=${apiKey}` : '' + }`, + ), + this._loadScript('https://unpkg.com/@googlemaps/markerclustererplus/dist/index.min.js'), + ]); + } + + apiLoadingPromise.then( + () => (this.hasLoaded = true), + error => console.error('Failed to load Google Maps API', error), + ); + } + + private _loadScript(url: string): Promise { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = url; + script.addEventListener('load', resolve); + script.addEventListener('error', reject); + document.body.appendChild(script); + }); + } } diff --git a/src/dev-app/index.html b/src/dev-app/index.html index b02a2bafb2c9..67ea3ff04fcc 100644 --- a/src/dev-app/index.html +++ b/src/dev-app/index.html @@ -17,20 +17,7 @@ Loading... - - - - diff --git a/src/dev-app/youtube-player/youtube-player-demo.ts b/src/dev-app/youtube-player/youtube-player-demo.ts index 4befbd9a0b01..81fe178e0b61 100644 --- a/src/dev-app/youtube-player/youtube-player-demo.ts +++ b/src/dev-app/youtube-player/youtube-player-demo.ts @@ -53,7 +53,9 @@ export class YouTubePlayerDemo implements AfterViewInit, OnDestroy { videoWidth: number | undefined; videoHeight: number | undefined; - constructor(private _changeDetectorRef: ChangeDetectorRef) {} + constructor(private _changeDetectorRef: ChangeDetectorRef) { + this._loadApi(); + } ngAfterViewInit(): void { this.onResize(); @@ -70,4 +72,14 @@ export class YouTubePlayerDemo implements AfterViewInit, OnDestroy { ngOnDestroy(): void { window.removeEventListener('resize', this.onResize); } + + private _loadApi() { + if (!window.YT) { + // We don't need to wait for the API to load since the + // component is set up to wait for it automatically. + const script = document.createElement('script'); + script.src = 'https://www.youtube.com/iframe_api'; + document.body.appendChild(script); + } + } }