8

How is it possible to load an external js file, from a url in an Angular component?

To be specific, I'm trying to load google-maps-api to my angular project. Currently, I'm doing it in my index.html like this:

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

Note: I'm aware of angular-maps. That's not an option.

Hamid Asghari
  • 5,751
  • 4
  • 24
  • 34

2 Answers2

14

You can use Promise to load google API asynchronously whenever you want.

// map-loader.service.ts
import ...
declare var window: any;

@Injectable()
export class MapLoader {

    private static promise;
    map: google.maps.Map;

    public static load() {
        if (!MapLoader.promise) { // load once
            MapLoader.promise = new Promise((resolve) => {
                window['__onGapiLoaded'] = (ev) => {
                    console.log('gapi loaded')
                    resolve(window.gapi);
                }
                console.log('loading..')
                const node = document.createElement('script');
                node.src = 'https://maps.googleapis.com/maps/api/js?key=<YOUR _API_KEY>&callback=__onGapiLoaded';
                node.type = 'text/javascript';
                document.getElementsByTagName('head')[0].appendChild(node);
            });
        }

        return MapLoader.promise;
    }

    initMap(gmapElement, lat, lng) {

        return MapLoader.load().then((gapi) => {
            this.map = new google.maps.Map(gmapElement.nativeElement, {
                center: new google.maps.LatLng(lat, lng),
                zoom: 12
            });
        });
    }
}

Component

//maps.component.ts

...
@Component({
  selector: 'maps-page',
  templateUrl: './maps.component.html'
})
export class MapsComponent implements OnInit {       

  @ViewChild('gmap') gmapElement: any;
  constructor(public mapLoader: MapLoader) { }

  ngOnInit() {

      this.mapLoader.initMap(this.gmapElement, -37.81, 144.96)  // await until gapi load
  }

  ...
}

Component HTML

// maps.component.html
<div #gmap ></div>

Don't forget to add CSS to the element.

John
  • 3,304
  • 1
  • 18
  • 26
  • 5
    I'm not sure why this answer wasn't upvoted. I got my solution by looking at this code -- but I implemented in app.component.ts in ngOnInit(). Cheers! – brandoncluff Apr 25 '18 at 22:28
  • replace "map: google.maps.Map;" with "map: any;" to prevent type errors. also add "window." before any "google.maps." – Hossein Oct 08 '18 at 12:33
  • @desmati, it is to use @types/googlemaps in typescript. It will support auto-completion of google API. If you have an error, you may install @types/googlemaps. – John Oct 09 '18 at 00:34
  • The main answer doesn't work for me but this works, only add to your css by id: #map { height: 100vh; width: 100vw; } Then add to node.src variable: node.src +='&key=YOUR_KEY'; – Carlos Jesus Arancibia Taborga Oct 31 '18 at 14:05
  • I presumed the person need this code, also know about CSS. Regarding key, google changed their policy and need API key. I will update the above code. – John Oct 31 '18 at 22:37
  • @John So, for adding markers we also create the function inside the service? It will be great if you can show how will you tackle adding, removing of Markers :) – Aslam Nov 26 '18 at 15:57
  • @Aslam, It does not have to be in the service. You can use MapLoader.load().then((gapi)... in wherever you need like ngOnInit in component. Also after create google.maps object, assign it to something like GmapStoreService to be used in others. – John Nov 26 '18 at 23:58
  • @John. I have used above code in my angular app and its working fine but i want to use some other thing as well like pinView and its not working. Anybody can help ? const pinViewScaled = google.maps.Marker.PinView({ scale: 1.5, }); const markerViewScaled = google.maps.Marker.AdvancedMarkerView({ map, position: { lat: 37.419, lng: -122.02 }, content: pinViewScaled.element, }); – akshay saxena Nov 17 '22 at 12:59
  • @akshaysaxena, it sounds that your question is not relevant to this topic. Would you be able to create a question with your code and leave the url to me? – John Nov 18 '22 at 21:07
7

A solution is to create the script tag dynamically in ngAfterViewInit()

import { DOCUMENT } from '@angular/common';
...

constructor(private @Inject(DOCUMENT) document, 
            private elementRef:ElementRef) {};

ngAfterViewInit() {
  var s = this.document.createElement("script");
  s.type = "text/javascript";
  s.src = "https://maps.googleapis.com/maps/api/js?key=API_KEY";
  this.elementRef.nativeElement.appendChild(s);
}

See also https://github.com/angular/angular/issues/4903

Update

<div id="map" #mapRef></div>

export class GoogleComponent implements OnInit {
  @ViewChild("mapRef") mapRef: ElementRef;
  constructor() { }

  ngOnInit() {
    this.showMap();
  }

  showMap() {
    console.log(this.mapRef.nativeElement);
    const location = { lat: 28.5355, lng: 77.3910 };
    var options = {
      center: location,
      zoom: 8
    }

    const map = new google.maps.Map(this.mapRef.nativeElement, options);
    this.addMarket(map, location);
  }
  addMarket(pos, map) {
    return new google.maps.Marker({
      position: pos,
      map: map,
    });
  }
}
Raphaël Balet
  • 6,334
  • 6
  • 41
  • 78
santosh singh
  • 27,666
  • 26
  • 83
  • 129