TypeScriptでGoogleのgapi.auth2からいくつかのクラスまたは関数をインポートしようとしました。しかし、以下のコードはタイピングディレクトリにgapi.auth2タイプを正しく追加しても機能しません。
import { GoogleAuth } from 'gapi.auth2';
私はいつもエラーになりました:
Error TS2307: Cannot find module 'gapi.auth2'
'../../typings/gapi.auth2'などの相対ディレクトリ検索を使用しますか?
それとも私がgapiを使っている方法が全く間違っているのでしょうか?
ありがとう!
Angular2でgapi
および_gapi.auth
_を使用するには、NPMを使用してタイプスクリプト定義をインストールします。
_npm install --save @types/gapi
npm install --save @types/gapi.auth2
_
これにより、 @ types/gapi および @types/gapi.auth2 の2つのパッケージが_node_modules
_フォルダーにインストールされ、 _package.json
_の構成。
_node_modules
_フォルダーを調べて、正しくインストールされていることを確認します。 Angular2アプリがmain-appと呼ばれる場合、次のように表示されます:
_main-app/
node_modules/
@types/
gapi/
gapi.auth2/
_
_tsconfig.json
_を編集して、新しいgapi
および_gapi.auth2
_型を含めます(以下は抜粋です):
_{
"compileOnSave": false,
"compilerOptions": {
"types": ["gapi", "gapi.auth2"]
}
}
_
この時点で、私はコーヒーを手に取って TypeScript Module Resolution を読むことを強くお勧めします。スキップしてNode.jsがモジュールを解決する方法:
[...]非相対モジュール名の解決は異なる方法で実行されます。 Nodeは、_
node_modules
_という名前の特別なフォルダーでモジュールを探します。 _node_modules
_フォルダーは、現在のファイルと同じレベルか、ディレクトリチェーンの上位に配置できます。 Nodeは、ロードしようとしたモジュールが見つかるまで、各_node_modules
_を調べながら、ディレクトリチェーンを調べます。
このため、Angular2サービスまたはコンポーネント(またはgapi
または_gapi.auth2
_を使用している場所)の型定義への参照を追加する必要はありません。
ただし、gapi
または_gapi.auth2
_ TypeScript定義への参照を追加する場合は、_.ts
_を使用してインストールされた_npm install
_ファイルを参照する必要があります(注、_///
_そうしないとエラーが発生します):
_/// <reference path="../../node_modules/@types/gapi/index.d.ts" />
_
パスは相対パスなので、_.ts
_ファイルがTypeScript定義をインストールした場所からの相対位置によって異なる場合があります。
明示的な参照を追加した場合でも、TypeScriptのNodeモジュール解決メカニズムを使用した場合でも、_.ts
_ファイルで変数を宣言して、コンパイル時にAngular2がウィンドウギャップ変数を認識できるようにする必要があります。 _declare var gapi: any;
_を_.ts
_ファイルに追加しますが、クラス定義内にしないでください。私は輸入品のすぐ下に私のものを置きます:
_// You may not have this explicit reference.
/// <reference path="../../node_modules/@types/gapi/index.d.ts" />
import { NgZone, Injectable, Optional } from '@angular/core';
declare var gapi: any;
_
定義自体( https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/gapi/index.d.ts )を見ると、関数のみがエクスポートされます。逆に、インターフェースは実装の詳細であるため、エクスポートされずに残り、名前空間外のコードからは見えません。
他のJavaScriptライブラリでの作業TypeScriptのドキュメント で、このすべての作業で何が得られるかを理解することは、読む価値があります。
次に、gapi
クライアントに独自の関数をロードします(Angularサービスで可能):
_ loadClient(): Promise<any> {
return new Promise((resolve, reject) => {
this.zone.run(() => {
gapi.load('client', {
callback: resolve,
onerror: reject,
timeout: 1000, // 5 seconds.
ontimeout: reject
});
});
});
}
_
この関数は自明ではありません、そして正当な理由で...
まず、コールバックだけでなく、構成オブジェクトを使用して gapi.load を呼び出していることに注意してください。 GAPIリファレンス 状態のいずれかを使用できます。
構成オプションを使用すると、ライブラリのタイムアウト時のエラー、またはエラーのみをrejectすることができます。私の経験では、ライブラリのロードは初期化よりも頻繁に失敗します。そのため、構成オブジェクトは単なるコールバックよりも優れています。
次に、_gapi.load
_をラップします
_this.zone.run(() => {
// gapi.load
});
_
NgZone.runはドキュメント化されています および状態
_
zone.run
_を介して関数を実行すると、Angularゾーン[...]の外部で実行されたタスクからAngularゾーンに再度入ることができます
_gapi.load
_の呼び出しがAngularゾーンを離れるので、これはまさに私たちが望むことです。これを省略すると、非常にファンキーな結果が残り、デバッグが困難になる可能性があります。
3番目に、loadClient()
は解決されたpromiseを返します-呼び出し元が_gapi.load
_の処理方法を選択できるようにします。たとえば、loadClient
メソッドがAngularサービスapiLoaderServce
に属している場合、コンポーネントはngOnInit
を使用してgapi
をロードできます。
_ngOnInit(): void {
this.apiLoaderService.loadClient().then(
result => this.apiLoaded = true,
err => this.apiLoaded = false
);
}
_
_gapi.load
_が呼び出されると、_gapi.client
_が準備できます。これを使用して、APIキー、OAuthクライアントID、スコープ、およびAPI検出ドキュメントでJavaScriptクライアントを初期化する必要がありますs):
_initClient(): Promise<any> {
var API_KEY = // Your API key.
var DISCOVERY_DOC = // Your discovery doc URL.
var initObj = {
'apiKey': API_KEY,
'discoveryDocs': [DISCOVERY_DOC],
};
return new Promise((resolve, reject) => {
this.zone.run(() => {
gapi.client.init(initObj).then(resolve, reject);
});
});
}
_
友人 NgZone.run をもう一度使用して、Angularゾーンに再度入るようにしています。
実際には、loadClient()
とinitClient()
をAngularサービスに追加します。高レベルのAngularコンポーネント(通常はapp-componentのすぐ下)でngOnInit
をロードして初期化します。
_ngOnInit(): void {
this.apiLoaderService.loadClient().then(
result => {
this.apiLoaded = true;
return this.apiLoaderService.initClient()
},
err => {
this.apiFailed = true;
}
).then(result => {
this.apiReady = true;
}, err => {
this.apiFailed = true;
});
}
_
最後に、gapiスクリプトファイルをファイルに追加する必要があります。
_<html>
<head>
<script src="https://apis.google.com/js/api.js"></script>
_
async
ordefer
属性を使用することはできません。いずれの場合も、gapiがロードされる前にAngular 2の世界に入るからです。
_<!-- This will not work. -->
<html>
<head>
<script async defer src="https://apis.google.com/js/api.js"></script>
_
以前に、 gapiライブラリ のローカルの縮小されたコピーを_/main-app/src/assests
_フォルダーに読み込んでインポートすることで、ページの読み込み速度を高速に保つことを提案しました:
_ <html>
<head>
<script src="assets/api.js"></script>
_
ただし、私は強く推奨しないこれを推奨します。 Googleが https://apis.google.com/js/api.js を更新する可能性があり、クライアントが機能しなくなります。私はこれに2回巻き込まれました。結局のところ、_//apis.google.com/js/
_からインポートし、それをブロッキング呼び出しとして保持することをお勧めします。
これは、RxJSライブラリを使用するように @ Jack's の回答から変更されています。元の質問ではAngular 2が求められますが、ここではAngular 5を使用しています。これは、誰かが更新されたバージョンで作業している場合に備えてです。
最初のステップは同じで、npmを使用してgapiタイプをダウンロードします。
_npm install --save @types/gapi
npm install --save @types/gapi.auth2
_
Tsconfig.jsonを更新する必要があります。問題がある場合は、tsconfig.app.jsonとtsconfig.spec.jsonも更新する必要がある場合があります。これらはtsconfig.jsonから継承しますが、タイプを指定すると、ベースが上書きされる可能性があると思います。以下のスニペット:
_"typeRoots": [
"node_modules/@types"
],
"types": [
"gapi",
"gapi.auth2"
],
"lib": [
"es2017",
"dom"
]
_
Googleの_platform.js
_への参照を追加します。私は_index.html
_に入れました。 @ Jack を推奨するため、async
とdefer
を省略しました。
_<script src="https://apis.google.com/js/platform.js"></script>
_
次に、認証サービスを作成します。完全なコードはここにあります:
_import { Injectable, NgZone, Output } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { User } from './User';
@Injectable()
export class AuthenticatorService {
public auth2: any;
public user$: BehaviorSubject<User> = new BehaviorSubject<User>(null);
public isLoggedIn$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public isLoaded$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
constructor(private zone: NgZone, private http: HttpClient) { }
validateToken(token: string): Observable<User> {
return this.http.get<User>(`http://yourServer:3000/validationApi/${token}`);
}
signIn(): void {
this.auth2.signIn().then(user => {
this.validateToken(user.getAuthResponse().id_token).subscribe(user => {
this.zone.run(() => {
this.user$.next(user);
this.isLoggedIn$.next(true);
});
},
(err) => {
console.error(err);
});
});
};
signOut(): void {
this.auth2.signOut().then(() => {
this.zone.run(() => {
this.isLoggedIn$.next(false);
this.user$.next(null);
});
},
(err) => {
console.error(err);
});
}
loadAuth2(): void {
gapi.load('auth2', () => {
gapi.auth2.init({
client_id: 'yourClientId',
fetch_basic_profile: true
}).then((auth) => {
this.zone.run(() => {
this.auth2 = auth;
this.isLoaded$.next(true);
});
},
);
});
}
}
_
ここではたくさんのことが起こっています。まず、RxJS BehaviorSubjectsに注目してください。これらを使用して、コンポーネントに変更を通知します。私たちの_loadAuth2
_関数は、Googleのライブラリを使用して_gapi.auth2.GoogleAuth
_オブジェクトを取得します。 Googleの認証ライブラリの詳細が必要な場合は、 their Introduction または their documentation を確認してください。 GoogleAuth
オブジェクトを取得したら、_this.zone.run
_を使用していることに注意してください。 NgZone
で関数全体を実行すると、予期しない動作が発生しました。次に、RxJS BehaviorSubject
_isLoaded$
_および値をtrueに設定します。 signIn()
関数とsignOut()
関数でも同様の動作が見られます。結果を取得してNgZone
で実行し、適切なBehaviorSubject
を更新します。
これでサービスを利用できるようになったので、それを使用します。サインインおよびサインアウトするためのコンポーネントを作成します。コードは以下のとおりです。
_import { Component, OnInit } from '@angular/core';
import { AuthenticatorService } from '../authenticator.service'
import { User } from '../User';
@Component({
selector: 'sign-in',
template: `
<ng-container *ngIf="authIsLoaded">
<button *ngIf="!isLoggedIn" (click)="signIn()">Sign In With Google</button>
<button *ngIf="isLoggedIn" (click)="signOut()">Sign Out</button>
</ng-container>
<h2 *ngIf="authIsLoaded && isLoggedIn"> Signed in as {{user.name}} </h2>`
})
export class GoogleAuthenticatorComponent implements OnInit {
public authIsLoaded: boolean = false;
public isLoggedIn: boolean = false;
public user: User;
constructor(private authenticatorService: AuthenticatorService) { }
signIn(): void {
this.authenticatorService.signIn();
};
signOut(): void {
this.authenticatorService.signOut();
}
ngOnInit() {
this.authenticatorService.isLoaded$.subscribe( value => {
this.authIsLoaded = value;
});
this.authenticatorService.isLoggedIn$.subscribe( value => {
this.isLoggedIn = value;
});
this.authenticatorService.user$.subscribe( value => {
this.user = value;
});
this.authenticatorService.loadAuth2();
}
}
_
ここで最も重要な部分はngOnInit
の実装です。ここで、AuthenticatorServiceの変更をサブスクライブし、それに応じてビューを更新します。
これらの手順が誰かがプロジェクトでgapi.auth2を設定するのに役立つことを願っています。