2016-02-20 3 views
30

Я создаю сайт, который имеет довольно стандартный веб-сервис RESTful для обработки настойчивости и сложной бизнес-логики. Пользовательский интерфейс, который я создаю для использования этой службы, использует Angular 2 с компонентами, написанными в TypeScript.Вход в Google для веб-сайтов и угловой 2 с использованием машинописного текста

Вместо того, чтобы создавать собственную систему аутентификации, я надеюсь положиться на вход в Google для веб-сайтов. Идея заключается в том, что пользователи придут на сайт, войдут в систему через предоставленную там инфраструктуру и затем отправят по итоговым ID-маркерам, которые затем проверит сервер, на котором размещена служба RESTful.

В документации для входа в систему Google есть instructions for creating the login button via JavaScript, что и должно произойти, так как кнопка входа в систему динамически создается в шаблоне с угловым выражением. Соответствующая часть шаблона:

<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> 

И Угловое определение 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); 
    } 
} 

Основной поток идет:

  1. Угловая делает шаблон и сообщение «Hello , пусто! " Показано.
  2. Подключен крючок ngAfterViewInit и вызывается метод gapi.signin2.render(...), который преобразует пустой div в кнопку входа в систему Google. Это работает правильно, и нажатие на эту кнопку вызывает процесс входа в систему.
  3. Это также придает onGoogleLoginSuccess метод компоненты фактически обрабатывать возвращенный маркер после входа пользователя в системе.
  4. Угловые обнаруживает, что userDisplayName свойства изменилось и обновляет страницу в настоящее время отображения «Привет, Крэйг (или что-то ваше имя)!».

Первая проблема, которая возникает в методе onGoogleLoginSuccess. Обратите внимание на вызовы console.log(...) в constructor и в этом методе. Как и ожидалось, тот, который находится в constructor, возвращает угловой компонент. Однако в методе 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 крючка:

setTimeout(function() { 
    this.googleLoginButtonId = this.googleLoginButtonId }, 
    5000); 

Это не должно действительно сделать что-нибудь. Он просто ждет пять секунд после того, как крючок заканчивается, а затем устанавливает значение свойства, равное самому себе.Однако, когда линия находится на месте, сообщение "Hello, empty!" превращается в "Hello, Craig!" примерно через пять секунд после загрузки страницы. Это подсказывает мне, что Angular просто не замечает, что значения свойств меняются в методе onGoogleLoginSuccess. Поэтому, когда что-то еще случается, чтобы уведомить 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(); 
    }); 
} 

я должен импортировать объект: import {Component, NgZone} from "angular2/core";

Я также необходимый для введения его как предложено в ответ через застройщик в класса: constructor(private _zone: NgZone) { }

+0

Новое в машинопись, где делает 'gapi' родом из? Я использую ваш пример для внедрения google login. Я объявляю var 'gapi' - но это пустой var? поэтому, пытаясь вызвать метод i, я получаю нагрузку на ошибки. Откуда возникает «щепа»? Какой бит мне не хватает? – Scironic

+1

Переменная gapi на самом деле является объектом, определенным в файле JavaScript API Google (https://apis.google.com/js/platform.js?onload=renderButton.) Вам нужно будет включить этот скрипт на странице, где скрипт запущен. Оператор declare просто делает его видимым для остальной части кода TypeScript, не создавая его. Если переменная является только пустым объектом для вас, я предполагаю, что этот сценарий должен быть включен. –

+0

Спасибо, что работает сейчас, мне также нужен заголовок google-signin-client-id с моим идентификатором клиента. – Scironic

ответ

19

для вашего первого решения проблемы заключается в использовании arrow function, которая будет сохранять контекст this:

onGoogleLoginSuccess = (loggedInUser) => { 
    this.userAuthToken = loggedInUser.getAuthResponse().id_token; 
    this.userDisplayName = loggedInUser.getBasicProfile().getName(); 
    console.log(this); 
    } 

Вторая проблема возникает, потому что сторонние скрипты выходят за пределы контекста Angular. Angular использует zones, поэтому, когда вы запускаете что-то, например setTimeout(), который обезглавлен для запуска в зоне, Angular получит уведомление. Вы бы запустить JQuery в зоне, как это:

constructor(private zone: NgZone) { 
    this.zone.run(() => { 
     $.proxy(this.onGoogleLoginSuccess, this); 
    }); 
    } 

Есть много вопросов/ответов о зоне с гораздо лучше объяснениями, то мой, если вы хотите знать больше, но это не должно быть проблемой для примера если вы используете функцию стрелки.

+0

Я использую этот же код. но я становлюсь пустым как 'userDisplayName' даже после того, как я вошел в систему –

+0

, здесь похоже, что обратный вызов никогда не вызывается .. – cesarpachon

+0

Привет, Это не работает iOS chrome –

6

Я сделал компонент 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); 
    }); 
    } 

Это немного поздно, но я просто хочу, чтобы дать пример, если кто-то хочет использовать Google для входа в API с NG2.

3

Включите ниже файл в index.html

<script src="https://apis.google.com/js/platform.js" async defer></script> 

login.html

<button id="glogin">google login</button> 

Войти.ц

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)); 
     }); 

} 
0

Попробуйте этот пакет - npm install angular2-google-login

GitHub - https://github.com/rudrakshpathak/angular2-google-login

Я реализовал Google Войти в Angular2. Просто импортируйте пакет, и вы готовы к работе.

Steps -

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 
}); 
+0

Я получаю ошибку «gapi is not defined». Попробовал исправить, просмотрев https://www.npmjs.com/package/angular2-google-login, но не смог. Как использовать AfterViewInit для этого компонента? Не могли бы вы объяснить это? –

 Смежные вопросы

  • Нет связанных вопросов^_^