Почему NgZone решает эту проблему?
Я создаю приложение, в котором пользователь может идентифицировать себя через свою учетную запись Google. За кулисами я использую gapi для обработки логина. С другой стороны, есть угловой сервис, называемый "пользователь", который имеет Observable
это передача информации подписчикам каждый раз, когда изменяется статус (онлайн / оффлайн) пользователя. Затем, чтобы склеить все вместе, есть кнопка, и когда пользователь нажимает на нее, вот что происходит:
- Создается обещание, которое разрешается после инициализации гапи. (1-я строка кода ниже)
- Когда обещание разрешается, вход в Google осуществляется через gapi. (2-я строка кода ниже)
- Когда обещание входа в систему разрешается, в бэкэнд делается идентификатор запроса AJAX, чтобы проверить токен Google и вернуть такую информацию, как адрес электронной почты, имя и т. Д., И подписаться на это наблюдаемое. (строка, начинающаяся с
this.restService
в коде ниже) - Когда наблюдаемое выше испускает значение, возвращаемое моим веб-сервисом, я вызываю функцию в моем "пользовательском" сервисе, которая выдает значение наблюдаемого для статуса пользователя (оно передает факт, что пользователь теперь аутентифицирован).
- Затем все подписчики получают эту информацию и знают, что пользователь вошел в систему.
Вот код:
this.ensureApiIsLoaded().then(() => {
this.auth.signIn().then((user) => {
let profile = user.getBasicProfile();
this.restService
.post("/api/security/authenticate", <IAuthenticationPayload>{ type: AuthenticationType.Google, token: user.getAuthResponse().id_token })
.subscribe((data: IUserData) => {
this.userService.set("data.name", "data.email", "data.picture", AuthenticationType.Google);
});
});
});
Дело в том, что код иногда работает, а иногда нет. После некоторого расследования я заметил, что это было связано с продолжительностью выполнения моего веб-сервиса. Чтобы убедиться в этом, я сделал внутри него заявление, которое приостанавливает выполнение запроса на 2 секунды, и в этом случае мой код всегда дает сбой. Но что я имею в виду под "неудачами"?
Где-то на странице у меня есть компонент, который подписан на наблюдаемый пользовательский сервис:
this.userService.getStatus().subscribe(status => {
console.log(status);
this.canPostComment = (status == UserStatus.Online);
});
Когда веб-служба выполняется очень быстро, я вижу console.log
тогда canPostComment
Свойство обновляется и мой взгляд, так что нет проблем. Тем не менее, когда веб-служба занимает немного больше времени, я все еще вижу console.log
с правильным значением, но представление не обновляется...
Подозревая, что это как-то связано с обнаружением угловых изменений, я использую зону следующим образом:
this.ensureApiIsLoaded().then(() => {
this.auth.signIn().then((user) => {
let profile = user.getBasicProfile();
this.zoneService.run(() => {
this.restService
.post("/api/security/authenticate", <IAuthenticationPayload>{ type: AuthenticationType.Google, token: user.getAuthResponse().id_token })
.subscribe((data: IUserData) => {
this.userService.set("data.name", "data.email", "data.picture", AuthenticationType.Google);
});
});
});
});
И тогда это работает... Итак, я прочитал о зоне, и я узнал, что она использовалась для предупреждения angular, что он должен запускать обнаружение изменений (или что-то подобное...), но я также читал, что общие функции, такие как setTimeout
или вызов AJAX уже пропатчен "зоной", поэтому я не понимаю, как он решает мою проблему, и я не понимаю, почему у меня есть эта проблема. Почему Angular этого не видит canPostComment
изменилось?
Так как мой код немного сложен, его было бы довольно сложно запрограммировать, поэтому я копирую / вставляю большую часть соответствующего кода.
РЕДАКТИРОВАТЬ
После того, как я задал вопрос, мне пришлось немного изменить свой код, потому что мне нужно было знать, когда завершится весь процесс входа в систему. Действительно, когда пользователь нажимает кнопку входа в систему, его заголовок становится "Вход в систему..." и после завершения всего процесса он исчезает. Я мог бы подписаться на userService.getStatus
Наблюдаемый, но я решил пойти с обещанием, чтобы быть в состоянии сделать:
this.googleAuthenticationService.login.then(() => { ... });
Поэтому я обновил код выше таким образом:
return new Promise((resolve, reject) => {
this.ensureApiIsLoaded().then(() => {
this.auth.signIn().then((user) => {
let profile = user.getBasicProfile();
this.zoneService.run(() => {
this.restService
.post("/api/security/authenticate", <IAuthenticationPayload>{ type: AuthenticationType.Google, token: user.getAuthResponse().id_token })
.subscribe((data: IUserData) => {
this.userService.set(data.name, data.email, data.picture, AuthenticationType.Google);
resolve(true)
},
error => reject(false));
});
});
});
});
Из любопытства я попытался удалить this.zoneService.run
заявление, чтобы увидеть, что происходит, и оказывается, что это работает без... Итак, все в обещании, кажется, решает проблему... Однако вопрос все еще остается... Почему первый пример не сработал?