Вход в Google для веб-сайтов и Angular 2 с использованием Typescript
Я создаю сайт с довольно стандартным веб-сервисом RESTful, который поддерживает постоянство и сложную бизнес-логику. Пользовательский интерфейс, который я создаю, чтобы использовать этот сервисУгловой 2 с компонентами, написанными на TypeScript.
Вместо того, чтобы создавать собственную систему аутентификации, я надеюсь использовать Google Sign-In для веб-сайтов. Идея состоит в том, что пользователи придут на сайт, войдут в систему через предоставленную инфраструктуру и затем отправят полученные 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>
И определение компонента Angular 2 в Typescript:
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);
}
}
Основной поток идет:
Angular отображает шаблон и сообщение «Привет, пусто!» Показано.ngAfterViewInit
крюк уволен иgapi.signin2.render(...)
вызывается метод, который преобразует пустой div в кнопку входа в Google. Это работает правильно, и нажатие на эту кнопку запустит процесс входа в систему.Это также придает компонентуonGoogleLoginSuccess
метод для фактической обработки возвращенного токена после входа пользователя в систему.Angular обнаруживает, чтоuserDisplayName
свойство изменилось и обновляет страницу, чтобы теперь отображать «Привет, Крейг (или как там тебя зовут)!».Первая проблема, которая возникает вonGoogleLoginSuccess
метод. Обратите внимание наconsole.log(...)
звонки вconstructor
и в этом методе. Как и ожидалось, один вconstructor
возвращает угловой компонент. Тот вonGoogleLoginSuccess
метод, однако, возвращает JavaScriptwindow
объект.
Похоже, что контекст теряется в процессе перехода к логике входа в систему Google, поэтому мой следующий шаг - попытаться включить jQuery$.proxy
позвоните, чтобы повесить на правильный контекст. Поэтому я импортирую пространство имен jQuery, добавивdeclare var $:any;
в верхней части компонента, а затем преобразовать содержимое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"
});
}
После добавления этого дваconsole.log
вызовы возвращают один и тот же объект, поэтому значения свойств теперь корректно обновляются. Второе сообщение журнала показывает объект с ожидаемыми обновленными значениями свойств.
К сожалению, шаблон Angular не обновляется, когда это происходит. Во время отладки я наткнулся на то, что, как мне кажется, объясняет, что происходит. Я добавил следующую строку в конецngAfterViewInit
крюк:
setTimeout(function() {
this.googleLoginButtonId = this.googleLoginButtonId },
5000);
Это не должно ничего делать. Он просто ждет пять секунд после завершения ловушки, а затем устанавливает значение свойства, равное самому себе. Тем не менее, с линией на месте"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) { }