我正在建立一个网站,有一个非常标准的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);
}
}
基本流转:
- 角渲染模板和消息“你好,空!“被显示。
ngAfterViewInit
钩子被激发,调用gapi.signin2.render(...)
方法将空div转换为Google登录按钮。这工作正常,点击该按钮将触发登录过程。- 这也重视组件的
onGoogleLoginSuccess
方法以实际处理用户登录后返回的标记英寸 - 角检测到
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) { }
新来打字稿,在那里做了'gapi'来自?我正在使用您的示例来实现Google登录。我声明var'gapi' - 但它是一个空的var对吗?所以当试图从我调用的方法我得到一个错误的负载。 'gapi'从哪里来?我错过了什么? – Scironic
gapi变量实际上是一个在Google API的JavaScript文件(https://apis.google.com/js/platform.js?onload=renderButton)中定义的对象。您需要将该脚本包含在脚本正在运行。 声明语句只是让它对其余的TypeScript代码可见,而不是创建它。 如果变量只是一个空对象,我的猜测是脚本需要包含在内。 –
感谢它现在的作品,我还需要谷歌登录客户端标识与我的客户端ID。 – Scironic