Я создаю сайт с довольно стандартным веб-сервисом RESTful для обработки настойчивости и сложной бизнес-логики. Пользовательский интерфейс, который я создаю для использования этой службы, использует Angular 2 с компонентами, написанными в TypeScript.
Вместо того, чтобы создавать свою собственную систему аутентификации, я надеюсь использовать Google Sign In 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 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 отображает шаблон и сообщение "Hello, empty!"..
- Захват
ngAfterViewInit
запущен и вызывается методgapi.signin2.render(...)
, который преобразует пустой div в кнопку входа в Google. Это работает правильно, и нажатие на эту кнопку вызывает процесс входа в систему. - Это также добавляет метод компонента
onGoogleLoginSuccess
для фактической обработки возвращаемого токена после входа пользователя в систему. - Angular обнаруживает, что свойство
userDisplayName
изменилось и обновляет страницу, на которой теперь отображается "Hello, Craig (или как бы то ни было ваше имя)!".
Первая проблема, возникающая в методе onGoogleLoginSuccess
. Обратите внимание на вызовы console.log(...)
в constructor
и в этом методе. Как и ожидалось, один из constructor
возвращает компонент Angular. Однако метод onGoogleLoginSuccess
возвращает объект JavaScript window
.
Таким образом, похоже, что контекст теряется в процессе перехода к логике входа в 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
hook:
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) { }