4

I am using Google API Javascript library in my Angular 2 application. I have created a service which is injected in components. Here is the code:

import { Injectable } from '@angular/core';
const url = 'https://apis.google.com/js/client.js?onload=__onGoogleLoaded';
@Injectable()
export class GoogleAPIService {
 public client: any;
 public calculatorService: any;
 public actionService: any;
loadAPI: Promise<any>
      constructor(){
        this.loadAPI = new Promise((resolve) => {
          window['__onGoogleLoaded'] = (ev) => {
            console.log('gapi loaded');
            resolve(window['gapi']);
            this.client = window['gapi'].client;
            this.loadEndPoints('{Endpoint URL}/_ah/api');
          }
          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);

      }

      loadEndPoints(apiRoot) {
  // Loads the OAuth and calculatorendpoint APIs asynchronously, and triggers login
  // when they have completed.
  var apisToLoad;
  var callback = function() {
    console.log('API Loaded '+apisToLoad);
    if (--apisToLoad == 0) {
      //this.endpoint1= this.client.endpoint1; //Doesn't Work
      //this.endpoint2= this.client.endpoint2; 
    }

  }

  apisToLoad = 3; // must match number of calls to gapi.client.load()
  this.client.load('oauth2', 'v2', callback);  
  this.client.load('endpoint1', 'v1', callback, apiRoot);
  this.client.load('endpoint2','v1',callback,apiRoot);

  }
}

I have three questions:

  1. How do I get the endpoints gapi.client.endpoint1 as a public variable in the service?
  2. How do I call the methods in the api? In javascript, u can jst call gapi.client.endpoint1.method().execute()
  3. How do I make this service singleton?

Any help is appreciated.

EDIT:

Here is the working version of the service. I use it as provider in my Root module. thus, its available as singleton throughout the application.

import { Injectable } from '@angular/core';

const url = 'https://apis.google.com/js/client.js?onload=__onGoogleLoaded';
const gapiOnLoaded = '__onGoogleLoaded';
const clientName = 'gapi';
const endpointhost = '[HTTPS URL FOR ENDPOINTS]';
const apiEndPoint = endpointhost + '/_ah/api';
@Injectable()
export class GoogleAPIService {
  private gapi: any;
  private loadAPI: Promise<any>;
  constructor() {
    this.loadAPI = new Promise((resolve) => {
        window[gapiOnLoaded] = (ev) => {
        this.gapi = window[clientName];
        // Loads the OAuth and other APIs asynchronously, and triggers login
        // when they have completed.
        let apisToLoad;
        let callback = function() {
        if (--apisToLoad === 0) {
            resolve(window[clientName]);
          }
        };
        apisToLoad = 3; // must match number of calls to gapi.client.load()
        this.gapi.load('client:auth2', callback);
        this.gapi.client.load('[ENDPOINT_1_NAME]', 'v1', callback, apiEndPoint);
        this.gapi.client.load('[ENDPOINT_2_NAME]', 'v1', callback, apiEndPoint);
      };
      this.loadScript();
    });
  }

  public GetClient(): any {
        return   this.loadAPI.then((res) => {
              return this.gapi;
          });
  }

  private loadScript()  {
    let node = document.createElement('script');
    node.src = url;
    node.type = 'text/javascript';
    document.getElementsByTagName('head')[0].appendChild(node);
  }
}

Inject this service in other services. I created a service for each of the endpoints.

@Injectable()
export class Endpoint1Service {
    private gapi: any;
    constructor(private googleApiService: GoogleAPIService) {
    }

    public isLoad() {
        return this.googleApiService.GetClient().then((gapi) => {
                this.gapi = gapi;
                return true;
            });
    }

    public action(data: DataType){
        this.gapi.client.endpointname.apimethod(data).execute();
    }
}
Akanksha Gaur
  • 2,636
  • 3
  • 26
  • 50
  • What do you mean by your first question? It looks like you already have a `const url = ...` to keep track of the google api url. What do you want to happen? – Federico Pettinella Sep 22 '16 at 09:52
  • I have multiple endpoints.. so i wanted to assign these to separate public variables.. so that I can use them in components as myservice.endpoint1service – Akanksha Gaur Sep 22 '16 at 10:05
  • @Akanksha This helped me a lot! Do you have an update on this? Perhaps you could post the newest version of your approach for connecting to Cloud Endpoints from Angular 2 (including the wrappers you mentioned in the reply to the accepted answer)? – jonasjuss Nov 11 '16 at 11:25
  • 1
    @jonasjuss I have posted the solution above. Hope it helps – Akanksha Gaur Nov 11 '16 at 11:40
  • @Akanksha Very helpful! Do you happen to have an example of calling action() from a component and returning an Observable / Promise for an array of items? I have a problem where my action() method returns undefined immediately, and cant get it to wait for the http response and return Observable or Promise with the items. – jonasjuss Nov 20 '16 at 16:40
  • 1
    @jonasjuss you will have to provide callback. It is recommended that you do not use gapi as it does not return observable. You should use HttpModule of Angular 2 to request data from google endpoints. – Akanksha Gaur Nov 21 '16 at 07:43

2 Answers2

1

Services are singletons by default. You should provide it in your AppModule, and then it will be available to all of your components. Just make sure to include it in your component constructors.

import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {HttpModule} from '@angular/http';

import { AppComponent } from './app.component';
import { routing } from './app.routing'; 
import { GoogleService } from './google.service'; // important

@NgModule({
    imports: [
        BrowserModule,
        HttpModule,
        routing,
    ],
    declarations: [ AppComponent],
    providers: [ GoogleService ], // important
    bootstrap: [ AppComponent],
})
export class  AppModule {
}

To make an endpoint available outside of your service, you can use the public keyword in front of the function that calls the endpoint. To call the endpoint in angular2, you can use the built-in http service from @angular/http. Here's an example service (only using HTTP GET) that will return an Observable for the endpoints you call.

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs';

@Injectable()
export class GoogleService {

    constructor(private http: Http) { }

    public endpoint1(): Observable<any> {
        return this.http.get("http://endpoint.one.com");
    }

    public endpoint2(): Observable<any> {
        return this.http.get("http://endpoint.two.com");
    }

}

You can then use the service like this in your component.

import { Component, OnInit } from '@angular/core';
import { GoogleService } from './google.service'; // important

@Component({
    selector: 'app',
    templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {
    constructor(private googleService: GoogleService) { }

    ngOnInit() {

        this.googleService.endpoint1().subscribe(callback1, handleError);

        this.googleService.endpoint2().subscribe(callback2, handleError);

    }

    callback1(data){
        // do something with the data from ONE
    }

    callback2(data){
        // do something with the data from TWO
    }

    handleError(error: any){
        console.error(error);
    }

}

I recommend reading up a bit on using Observables in this post from Angular University.

Federico Pettinella
  • 1,471
  • 1
  • 11
  • 19
0

I don't think modifying the DOM to load gapi is particularly good practice. It's probably better to use gapi and gapi.auth TypeScript definitions by installing them with NPM.

I've posted instructions on how to do this in my answer to Import gapi.auth2 in angular 2 typescript.

Community
  • 1
  • 1
Jack
  • 10,313
  • 15
  • 75
  • 118