好的,所以我将在这里发布我最终学习和所做的事情,以便其他人可以将其用作参考。我也会尽力解释正在发生的事情。
从其他评论中可以看出,传递给 Accounts.on****Link 回调的“done”函数是让我绊倒的主要部分。该函数只做一件事 - 重新启用自动登录。值得注意的是,“done”函数/autoLogin 是核心“accounts”包之一的一部分,无法修改。 “autoLogin”用于一种特定情况:用户 A 尝试在用户 B 当前登录的计算机上重置其密码。如果用户 A 在提交新密码之前退出重置密码流程,则用户 B 将保持登录状态如果用户 A 完成重置密码流程,则用户 B 会注销,用户 A 会登录。
用于处理 account-ui 包中“完成”的模式,以及我最终所做的,将“完成”分配给一个变量,然后可以将该变量传递给模板事件处理函数,并在重置密码逻辑完成后运行。此变量分配需要在 Accounts.on****Link 回调中完成,但回调可以放置在任何顶级客户端代码中(只需确保正确分配变量的范围)。我只是将其放在 reset_password_template.js 文件的开头(到目前为止我只是为了重置密码而这样做,但模式应该类似):
客户端/reset_password_template.js:
// set done as a variable to pass
var doneCallback;
Accounts.onResetPasswordLink(function(token, done) {
Session.set('resetPasswordToken', token); // pull token and place in a session variable, so it can be accessed later
doneCallback = done; // Assigning to variable
});
使用这些 on****Link 回调的另一个挑战是了解您的应用程序如何“知道”回调已被触发,以及应用程序需要执行哪些操作。由于iron:router 与Meteor 紧密集成,因此很容易忘记它是一个单独的包。重要的是要记住这些回调是为了独立于iron:router而编写的。这意味着当单击发送到您的电子邮件的链接时,您的应用程序会在根级别(“/”)加载。
***旁注 - StackOverflow 上还有一些其他答案,提供了与 Iron:router 集成的方法,并为每个链接加载特定路由。对我来说,这些模式的问题是它们看起来有点老套,不符合“流星”方式。更重要的是,如果 Meteor 核心团队决定改变这些注册链接的路径,这些路由就会中断。我尝试调用 Router.go('path');在 on****Link 回调中,但由于某种原因,这在 Chrome 和 Safari 中不起作用。我希望有一种方法来处理每个电子邮件链接的特定路由,从而消除不断设置和清除会话变量的需要,但我想不出一个有效的解决方案。
无论如何,正如 @stubailo 在他的回答中所描述的,您的应用程序已加载(在根级别),并且回调被触发。一旦回调被触发,您就设置了会话变量。您可以使用此会话变量使用以下模式在根级别加载适当的模板:
client/home.html(或您的登陆页面模板)
{{#unless resetPasswordToken}}
{{> home_template}}
{{else}}
{{> reset_password_template}}
{{/unless}}
这样,您需要在 reset_password_template.js 文件和 home.js 中注意一些事项:
客户端/home.js
// checks if the 'resetPasswordToken' session variable is set and returns helper to home template
Template.home.helpers({
resetPasswordToken: function() {
return Session.get('resetPasswordToken');
}
});
客户端/reset_password_template.js
// if you have links in your template that navigate to other parts of your app, you need to reset your session variable before navigating away, you also need to call the doneCallback to re-enable autoLogin
Template.reset_password_template.rendered = function() {
var sessionReset = function() {
Session.set('resetPasswordToken', '');
if (doneCallback) {
doneCallback();
}
}
$("#link-1").click(function() {
sessionReset();
});
$('#link2').click(function() {
sessionReset();
});
}
Template.reset_password_template.events({
'submit #reset-password-form': function(e) {
e.preventDefault();
var new_password = $(e.target).find('#new-password').val(), confirm_password = $(e.target).find('#confirm-password').val();
// Validate passwords
if (isNotEmpty(new_password) && areValidPasswords(new_password, confirm_password)) {
Accounts.resetPassword(Session.get('resetPasswordToken'), new_password, function(error) {
if (error) {
if (error.message === 'Token expired [403]') {
Session.set('alert', 'Sorry, this link has expired.');
} else {
Session.set('alert', 'Sorry, there was a problem resetting your password.');
}
} else {
Session.set('alert', 'Your password has been changed.'); // This doesn't show. Display on next page
Session.set('resetPasswordToken', '');
// Call done before navigating away from here
if (doneCallback) {
doneCallback();
}
Router.go('web-app');
}
});
}
return false;
}
});
希望这对尝试构建自己的自定义身份验证表单的其他人有所帮助。其他答案中提到的包对于许多情况都很有用,但有时您需要通过包无法提供的额外自定义。