12

I recently got started with ng2. So I created a project using angular-cli, then I realized I need some third party modules. Such as Google maps api, lodash, jquery...etc. I know the basics of Typescript, but how do I use these modules in an Angular2 app? Also, I want to use just the library, for example Google maps api, not any existing ng2 module/component someone else made ontop of the Google maps api - For the sake of learning.

Previously, with just JS I would include the js file and reference the api documentation to know which methods to reference and build my app. Now with Angular2, what steps should I take to do the same?

From my research it looks like I first install the type script files needed. so for Google maps I did npm install --save @types/google-maps. Now, do I need to import this module into my angular app by including it in app.module.ts? In the imports array? Or is it now globally available?

One source mentioned installing it with npm and in my angular-cli.json including a reference to that library in the scripts array. like so:

"scripts": ['./node_modules/googlemaps/googemaps.min.js'],

Which method to use in installing the google maps api? I would think Typescript might be the way to go since the rest of the Angular app is going to be written in Typescript.

Now in my app.component.ts, I want to create a simple map using the typescript I installed. How would I do so? The google maps api says to create a new instance of the map like so.

var map = new google.maps.Map(document.getElementById('map'), {
          center: {lat: -34.397, lng: 150.644},
          scrollwheel: false,
          zoom: 8
        });

How would I do that with the typescript version of Googlemaps I just installed?

Would a typescript version of Googlemaps have all the same methods as the original API? Do typescripts of popular JS libraries have a documentation site I can reference?

3
  • Man... Good luck with that! angular2-google-maps was made as a wrapper to help you avoid the pain you're about to go through. ;) Not saying it can't be done, but agm makes it much more manageable. Commented Mar 15, 2017 at 18:31
  • 1
    Don't think it should be to hard just integrating an existing API into my Angular2 project. More so a question about the steps involved in integrating and using third-party modules like GoogleMaps, Jquery, or Lodash through Typescript. Not trying to reinvent or create my own wrapper, not yet anyways. Commented Mar 16, 2017 at 19:45
  • 1
    Not saying it's necessarily hard per se, but it is tedious herding all of those rabbits. Don't let my comment dissuade/discourage you... it is a great learning experience! I did the same with a few less robust APIs/libraries. Commented Mar 16, 2017 at 19:53

2 Answers 2

17

The agm project pointed by @Steve G. give a good starting point, but for some reason (like manage your own request policy) you may want to make your own typed wrapper. Here how I did it with Angular Cli :

First step :

npm i --save @types/googlemaps

Secondly add the types to your tsconfig.app.json :

"types": ["googlemaps"]

Finally write your typescript :

//nothing to import here

//Replace this by anything without an ID_KEY
const getScriptSrc = (callbackName) => {
  return `https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=${callbackName}`;
}

export class GMapService {

  private map: google.maps.Map;
  private geocoder: google.maps.Geocoder;
  private scriptLoadingPromise: Promise<void>;

  constructor() {
        //Loading script
        this.loadScriptLoadingPromise();
        //Loading other components
        this.onReady().then(() => {
          this.geocoder = new google.maps.Geocoder();
        });
  }

  onReady(): Promise<void> {
    return this.scriptLoadingPromise;
  }

  initMap(mapHtmlElement: HTMLElement, options: google.maps.MapOptions): Promise<google.maps.Map> {
    return this.onReady().then(() => {
      return this.map = new google.maps.Map(mapHtmlElement, options);
    });
  }

  private loadScriptLoadingPromise() {
    const script = window.document.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.defer = true;
    const callbackName: string = 'UNIQUE_NAME_HERE';
    script.src = getScriptSrc(callbackName);
    this.scriptLoadingPromise = new Promise<void>((resolve: Function, reject: Function) => {
      (<any>window)[callbackName] = () => { resolve(); };

      script.onerror = (error: Event) => { reject(error); };
    });
    window.document.body.appendChild(script);
  }

  /** Exemple of wrapped to promise API */
  geocode(address: string | google.maps.GeocoderRequest): Promise<google.maps.GeocoderResult[]> {
    return this.onReady().then(() => {
      this.geocoder.geocode(typeof address == "google.maps.GeocoderRequest" ? address: {address: address},
          (results: google.maps.GeocoderResult[], status: google.maps.GeocoderStatus) => {
            if(status == google.maps.GeocoderStatus.OK) {
              return results;
            } else {
              throw new Error(status.toString());
            }
      });
    });
  });

}

And so your map component will look like this :

@Component({
  selector: 'app-gmap-wrapper',
  template: '<div #map style="width:400px; height:300px">loading...</div>'
})
export class GMapWrapperComponent implements OnInit {
    @ViewChild('map') mapRef: ElementRef;

    constructor(private gmapService: GMapService) { }

    ngOnInit() {
      this.gmapService.initMap(this.mapRef.nativeElement, {
        center: {lat: 1234.1234, lng: 1234.1234},
        scrollwheel: true,
        zoom: 8
      });
    }
}

This code should be better without the namespace google.maps in prefix of all types. Maybe any suggestion ?

Sign up to request clarification or add additional context in comments.

15 Comments

You can call onReady, initMap and geocode from any component. I edited the code with your review. Thanks
Took another look at your code & picked up a few issues: (1) In the geocode function you return the onReady() function, which returns a Promise<void>, however you set the return type of the geocode function to Promise<google.maps.GeocoderResult[]> which won't compile, I suggest possibly wrapping the inner geocode call in a Promise<google.maps.GeocoderResult[]> and return that; (2) throw new Error(status); this should read: throw new Error(status.toString());. I'll make the edit for that, not sure if you had something else in mind for the first point :)
Oh and happened to stumble across this: If you dont want to prefix everything with 'google.maps.....', you can add an import for the item you're trying to reference at the top of your file like so: import {GeocoderResult} = google.maps.GeocoderResult;
Sorry I edited it too quickly. Will fix later. For the import if you try it you will have a problem because it will be resolved before the namespace loading in Javascript...
Hmmm that's odd, I've implemented the namespace I suggested without issue and had it build successfully using AOT without any issues
|
0

I also had troubles with AGM in componets, then i tried nu-maps and also there I had troubles.

So I used plain google api javascript and I think its better: https://cloud.google.com/maps-platform/?hl=de

  • simple
  • good documented
  • not dependent of some guys who do not merge the pull requests
  • it works!

Comments