2016-02-20 46 views
30

我正在建立一个网站,有一个非常标准的RESTful Web服务来处理持久性和复杂的业务逻辑。我正在构建的用于使用此服务的UI使用Angular 2以及用TypeScript编写的组件。谷歌登录的网站和Angular 2使用手稿

我不希望构建自己的身份验证系统,而是希望依靠Google Sign-In for Websites。用户将访问该网站的想法是通过提供的框架进行登录,然后发送生成的标识令牌,然后托管RESTful服务的服务器可以对其进行验证。

在Google登录文档中有instructions for creating the login button via 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> 

而且在打字稿的角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. 角渲染模板和消息“你好,空!“被显示。
  2. ngAfterViewInit钩子被激发,调用gapi.signin2.render(...)方法将空div转换为Google登录按钮。这工作正常,点击该按钮将触发登录过程。
  3. 这也重视组件的onGoogleLoginSuccess方法以实际处理用户登录后返回的标记英寸
  4. 角检测到userDisplayName属性已更改和更新页面现在显示“Hello,克雷格(或任何你的名字是)!”。

发生的第一个问题是onGoogleLoginSuccess方法。请注意0​​和该方法中的console.log(...)调用。如预期的那样,constructor中的那个返回Angular组件。但是,onGoogleLoginSuccess方法中的那个返回JavaScript window对象。

因此,看起来上下文在跳出Google登录逻辑的过程中正在消失,所以下一步就是尝试将jQuery的$.proxy调用挂在正确的上下文中。所以我加入declare var $:any;到组件的进口顶级jQuery的命名空间,然后转换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, Craig!"。这表明Angular并没有注意到方法中的属性值正在改变。因此,当其他事情发生时通知Angular属性值已经发生变化(例如上面的无用自我赋值),Angular会唤醒并更新所有内容。

很明显,这不是一个黑客,我想离开,所以我想知道是否有任何角度的专家可以告诉我吗?是否有人打电话让Angular注意到一些属性已经改变?

已更新2016年2月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登录。我声明var'gapi' - 但它是一个空的var对吗?所以当试图从我调用的方法我得到一个错误的负载。 'gapi'从哪里来?我错过了什么? – Scironic

+1

gapi变量实际上是一个在Google API的JavaScript文件(https://apis.google.com/js/platform.js?onload=renderButton)中定义的对象。您需要将该脚本包含在脚本正在运行。 声明语句只是让它对其余的TypeScript代码可见,而不是创建它。 如果变量只是一个空对象,我的猜测是脚本需要包含在内。 –

+0

感谢它现在的作品,我还需要谷歌登录客户端标识与我的客户端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 Chromerome –

6

我做了一个谷歌登录组件,如果你想要一个例子。

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

这有点迟,但我只是想举一个例子,如果有人想使用谷歌登录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> 

登录。TS

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

我实现了谷歌在登录Angular2。只需导入包装,即可开始使用。

步骤 -

import { AuthService, AppGlobals } from 'angular2-google-login';

供应商 - providers: [AuthService];

构造 - constructor(private _googleAuth: AuthService){}

集谷歌客户端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这个组件?你能解释一下吗? –