Вход в 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) { }

Ответы на вопрос(4)

Ваш ответ на вопрос