永続性と複雑なビジネスロジックを処理するための、かなり標準的なRESTful Webサービスを備えたサイトを構築しています。このサービスを利用するために構築しているUIは、TypeScriptで記述されたコンポーネントで Angular 2 を使用しています。
独自の認証システムを構築するのではなく、ウェブサイトにGoogleサインインを使用したいと考えています。ユーザーがサイトにアクセスし、そこに提供されているフレームワークを介してサインインし、結果のIDトークンを送信して、RESTfulサービスをホストするサーバーが検証できるようにするという考えです。
Googleサインインのドキュメントには、 JavaScriptを使用してログインボタンを作成するための指示 があります。これは、ログインボタンがAngularテンプレートで動的にレンダリングされるために必要なことです。テンプレートの関連部分:
<div class="login-wrapper">
<p>You need to log in.</p>
<div id="{{googleLoginButtonId}}"></div>
</div>
<div class="main-application">
<p>Hello, {{userDisplayName}}!</p>
</div>
そして、TypeScriptのAngular 2コンポーネント定義:
import {Component} from "angular2/core";
// Google's login API namespace
declare var gapi:any;
@Component({
selector: "sous-app",
templateUrl: "templates/sous-app-template.html"
})
export class SousAppComponent {
googleLoginButtonId = "google-login-button";
userAuthToken = null;
userDisplayName = "empty";
constructor() {
console.log(this);
}
// Angular hook that allows for interaction with elements inserted by the
// rendering of a view.
ngAfterViewInit() {
// Converts the Google login button stub to an actual button.
api.signin2.render(
this.googleLoginButtonId,
{
"onSuccess": this.onGoogleLoginSuccess,
"scope": "profile",
"theme": "dark"
});
}
// Triggered after a user successfully logs in using the Google external
// login provider.
onGoogleLoginSuccess(loggedInUser) {
this.userAuthToken = loggedInUser.getAuthResponse().id_token;
this.userDisplayName = loggedInUser.getBasicProfile().getName();
console.log(this);
}
}
基本的な流れは次のとおりです。
ngAfterViewInit
フックが起動され、空のdivをGoogleログインボタンに変換するgapi.signin2.render(...)
メソッドが呼び出されます。これは正しく機能し、そのボタンをクリックするとログインプロセスがトリガーされます。onGoogleLoginSuccess
メソッドをアタッチして、ユーザーがログインした後に返されたトークンを実際に処理します。userDisplayName
プロパティが変更されたことを検出し、ページを更新して「Hello、Craig(またはあなたの名前は何でも)!」を表示します。最初に発生する問題は、onGoogleLoginSuccess
メソッドにあります。 constructor
およびそのメソッドのconsole.log(...)
呼び出しに注意してください。予想どおり、constructor
の1つはAngularコンポーネントを返します。ただし、onGoogleLoginSuccess
メソッドの1つは、JavaScript window
オブジェクトを返します。
そのため、Googleのログインロジックに飛び移る過程でコンテキストが失われているように見えるので、次のステップはjQueryの$.proxy
呼び出しを組み込んで正しいコンテキストに固執することです。コンポーネントの先頭にdeclare var $:any;
を追加してjQuery名前空間をインポートし、ngAfterViewInit
メソッドの内容を次のように変換します。
// Angular hook that allows for interaction with elements inserted by the
// rendering of a view.
ngAfterViewInit() {
var loginProxy = $.proxy(this.onGoogleLoginSuccess, this);
// Converts the Google login button stub to an actual button.
gapi.signin2.render(
this.googleLoginButtonId,
{
"onSuccess": loginProxy,
"scope": "profile",
"theme": "dark"
});
}
それを追加した後、2つのconsole.log
呼び出しは同じオブジェクトを返すため、プロパティ値は正しく更新されます。 2番目のログメッセージには、予想される更新されたプロパティ値を持つオブジェクトが表示されます。
残念ながら、Angularテンプレートはこれが発生しても更新されません。デバッグ中、私は何が起こっているのかを説明していると思う何かにつまずいた。 ngAfterViewInit
フックの最後に次の行を追加しました。
setTimeout(function() {
this.googleLoginButtonId = this.googleLoginButtonId },
5000);
これは実際には何もしません。フックが終了してから5秒待ってから、それ自体に等しいプロパティ値を設定します。ただし、行を配置すると、ページが読み込まれてから約5秒後に"Hello, empty!"
メッセージが"Hello, Craig!"
に変わります。これは、AngularがonGoogleLoginSuccess
メソッドでプロパティ値が変化していることに気付いていないことを示唆しています。したがって、プロパティ値が変更されたことを他の何かがAngularに通知すると(上記のような無用な自己割り当てなど)、Angularが起動してすべてを更新します。
明らかにそれは私がその場に残したいハックではないので、そこにいるAngular専門家が私を助けてくれるかどうか疑問に思っていますか? Angularにいくつかのプロパティが変更されたことを強制的に通知するための呼び出しがありますか?
2016-02-21を更新して、問題を解決した具体的な回答を明確にしました
最終的に、選択した回答で提供された提案の両方を使用する必要がありました。
まず、提案どおりに、矢印関数を使用するようにonGoogleLoginSuccess
メソッドを変換する必要がありました。次に、NgZone
オブジェクトを使用して、プロパティの更新がAngularが認識しているコンテキストで発生したことを確認する必要がありました。最終的な方法は次のようになりました
onGoogleLoginSuccess = (loggedInUser) => {
this._zone.run(() => {
this.userAuthToken = loggedInUser.getAuthResponse().id_token;
this.userDisplayName = loggedInUser.getBasicProfile().getName();
});
}
_zone
オブジェクトをインポートする必要がありました:import {Component, NgZone} from "angular2/core";
また、クラスのコンストラクターを介して答えで示唆されているように、それを注入する必要がありました:constructor(private _zone: NgZone) { }
最初の問題の解決策は arrow function を使用することです。これはthis
のコンテキストを保持します:
_ onGoogleLoginSuccess = (loggedInUser) => {
this.userAuthToken = loggedInUser.getAuthResponse().id_token;
this.userDisplayName = loggedInUser.getBasicProfile().getName();
console.log(this);
}
_
サードパーティのスクリプトがAngularのコンテキスト外で実行されるため、2番目の問題が発生しています。 Angularはzones
を使用するので、何かを実行するとき、たとえばsetTimeout()
はゾーンで実行するようにモンキーパッチを適用し、Angularは通知されます。jQueryは次のようにゾーンで実行します。
_ constructor(private zone: NgZone) {
this.zone.run(() => {
$.proxy(this.onGoogleLoginSuccess, this);
});
}
_
ゾーンに関する質問/回答はたくさんありますが、もっと詳しく知りたい場合は、私の説明よりもはるかに良い説明がありますが、矢印機能を使用する場合、例の問題にはなりません。
例が必要な場合は、google-loginコンポーネントを作成しました。
ngOnInit()
{
this.initAPI = new Promise(
(resolve) => {
window['onLoadGoogleAPI'] =
() => {
resolve(window.gapi);
};
this.init();
}
)
}
init(){
let meta = document.createElement('meta');
meta.name = 'google-signin-client_id';
meta.content = 'xxxxx-xxxxxx.apps.googleusercontent.com';
document.getElementsByTagName('head')[0].appendChild(meta);
let node = document.createElement('script');
node.src = 'https://apis.google.com/js/platform.js?onload=onLoadGoogleAPI';
node.type = 'text/javascript';
document.getElementsByTagName('body')[0].appendChild(node);
}
ngAfterViewInit() {
this.initAPI.then(
(gapi) => {
gapi.load('auth2', () =>
{
var auth2 = gapi.auth2.init({
client_id: 'xxxxx-xxxxxx.apps.googleusercontent.com',
cookiepolicy: 'single_Host_Origin',
scope: 'profile email'
});
auth2.attachClickHandler(document.getElementById('googleSignInButton'), {},
this.onSuccess,
this.onFailure
);
});
}
)
}
onSuccess = (user) => {
this._ngZone.run(
() => {
if(user.getAuthResponse().scope ) {
//Store the token in the db
this.socialService.googleLogIn(user.getAuthResponse().id_token)
} else {
this.loadingService.displayLoadingSpinner(false);
}
}
);
};
onFailure = (error) => {
this.loadingService.displayLoadingSpinner(false);
this.messageService.setDisplayAlert("error", error);
this._ngZone.run(() => {
//display spinner
this.loadingService.displayLoadingSpinner(false);
});
}
少し遅れていますが、ng2でgoogle login apiを使用したい場合の例を挙げたいと思います。
Index.htmlに以下のファイルを含めます
<script src="https://apis.google.com/js/platform.js" async defer></script>
login.html
<button id="glogin">google login</button>
login.ts
declare const gapi: any;
public auth2:any
ngAfterViewInit() {
gapi.load('auth2', () => {
this.auth2 = gapi.auth2.init({
client_id: '788548936361-h264uq1v36c5ddj0hf5fpmh7obks94vh.apps.googleusercontent.com',
cookiepolicy: 'single_Host_Origin',
scope: 'profile email'
});
this.attachSignin(document.getElementById('glogin'));
});
}
public attachSignin(element) {
this.auth2.attachClickHandler(element, {},
(loggedInUser) => {
console.log( loggedInUser);
}, function (error) {
// alert(JSON.stringify(error, undefined, 2));
});
}
このパッケージを試してください-_npm install angular2-google-login
_
Github- https://github.com/rudrakshpathak/angular2-google-login
Angular2にGoogleログインを実装しました。パッケージをインポートするだけで準備完了です。
手順-
_import { AuthService, AppGlobals } from 'angular2-google-login';
_
供給プロバイダー-_providers: [AuthService];
_
コンストラクタ-constructor(private _googleAuth: AuthService){}
GoogleクライアントIDを設定する-_AppGlobals.GOOGLE_CLIENT_ID = 'SECRET_CLIENT_ID';
_
これを使用してサービスを呼び出します-
_this._googleAuth.authenticateUser(()=>{
//YOUR_CODE_HERE
});
_
ログアウトするには-
_this._googleAuth.userLogout(()=>{
//YOUR_CODE_HERE
});
_
Sasxaによる選択された答えも私を助けましたが、。bind(this)を使用してこれをonSuccess関数にバインドできることがわかりました。そのように、太い矢印で関数を作成する必要はありません。
ngAfterViewInit() {
var loginProxy = $.proxy(this.onGoogleLoginSuccess, this);
// Converts the Google login button stub to an actual button.
gapi.signin2.render(
this.googleLoginButtonId,
{
"onSuccess": loginProxy.bind(this),
"scope": "profile",
"theme": "dark"
});
}