【问题标题】:Meteor Iron router hooks being run multiple timesMeteor Iron 路由器钩子被多次运行
【发布时间】:2015-03-14 10:28:09
【问题描述】:

编辑: Here 是 github 存储库。您可以测试网站here

在主页上,只需打开浏览器控制台,您就会注意到WaitOndata 正在运行两次。当没有WaitOn 时,数据只运行一次。


我通过扩展RouteController 并进一步扩展这些控制器来设置我的页面。例如:

    ProfileController = RouteController.extend({
        layoutTemplate: 'UserProfileLayout',
        yieldTemplates: {
            'navBarMain': {to: 'navBarMain'},
            'userNav': {to: 'topUserNav'},
            'profileNav': {to: 'sideProfileNav'}
        },
        // Authentication
        onBeforeAction: function() {
            if(_.isNull(Meteor.user())){
              Router.go(Router.path('login'));
            } else {
                this.next();
            } 
          }
     });

ProfileVerificationsController = ProfileController.extend({
    waitOn: function() {
        console.log("from controller waitOn");
        return Meteor.subscribe('userProfileVerification');
    },

    data: function() {
        // If current user has verified email
        console.log("from controller data start");
        var verifiedEmail = Meteor.user().emails && Meteor.user().emails[0].verified ? Meteor.user().emails[0].address : '';
        var verifiedPhoneNumber = Meteor.user().customVerifications.phoneNumber && Meteor.user().customVerifications.phoneNumber.verified ? Meteor.user().customVerifications.phoneNumber.number : '';

        var data = {
            verifiedEmail: verifiedEmail,
            verifiedPhoneNumber: verifiedPhoneNumber
        };
        console.log("from controller data end");
        return data;
    }
});

在客户端观察控制台时,似乎钩子正在运行 2-3 次。由于数据不可用,我也有一次遇到错误。以下是仅请求页面一次的控制台:

from controller waitOn
profileController.js?966260fd6629d154e38c4d5ad2f98af425311b71:44 from controller data start
debug.js:41 Exception from Tracker recompute function: Cannot read property 'phoneNumber' of undefined
TypeError: Cannot read property 'phoneNumber' of undefined
    at ProfileController.extend.data (http://localhost:3000/lib/router/profileController.js?966260fd6629d154e38c4d5ad2f98af425311b71:46:62)
    at bindData [as _data] (http://localhost:3000/packages/iron_controller.js?b02790701804563eafedb2e68c602154983ade06:226:50)
    at DynamicTemplate.data (http://localhost:3000/packages/iron_dynamic-template.js?d425554c9847e4a80567f8ca55719cd6ae3f2722:219:50)
    at http://localhost:3000/packages/iron_dynamic-template.js?d425554c9847e4a80567f8ca55719cd6ae3f2722:252:25
    at null.<anonymous> (http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:2445:26)
    at http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:1808:16
    at Object.Blaze._withCurrentView (http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:2043:12)
    at viewAutorun (http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:1807:18)
    at Tracker.Computation._compute (http://localhost:3000/packages/tracker.js?517c8fe8ed6408951a30941e64a5383a7174bcfa:296:36)
    at Tracker.Computation._recompute (http://localhost:3000/packages/tracker.js?517c8fe8ed6408951a30941e64a5383a7174bcfa:310:14)
from controller data start
from controller data end
from controller waitOn
from controller data start
from controller data end

我没有正确使用控制器吗?

【问题讨论】:

    标签: javascript meteor iron-router


    【解决方案1】:

    由于无法查看您定义的使用这些路由控制器的其余代码(例如模板或路由定义),我无法准确说明数据函数被多次调用的原因。我怀疑您可能将ProfileVerificationsController 与多个路由一起使用,在这种情况下,此控制器的data 定义将被执行多次,每个使用该控制器的路由执行一次。由于data 定义是反应式的,当您浏览应用程序和数据更改时,这可能会导致定义的代码重新运行。

    至于您的控制器定义,我建议您进行一些修改以使代码更加健壮和防弹。一、ProfileController定义:

        ProfileController = RouteController.extend({
            layoutTemplate: 'UserProfileLayout',
            yieldRegions: {
                'navBarMain': {to: 'navBarMain'},
                'userNav': {to: 'topUserNav'},
                'profileNav': {to: 'sideProfileNav'}
            },
            onBeforeAction: function() {
                if(!Meteor.user()) {
                    Router.go(Router.path('login'));
                    this.redirect('login'); // Could do this as well
                    this.render('login'); // And possibly this is necessary
                } else {
                    this.next();
                }
            }
        });
    

    请注意我更改的第一件事,yieldTemplatesyieldRegions。这种错字会阻止使用此路由控制器的模板中的区域正确填充所需的子模板。其次,在onBeforeAction 定义中,我建议不仅使用下划线检查Meteor.user() 对象是否为null,而且还要检查它是否为undefined。我所做的修改将允许您检查Meteor.user() 对象的两种状态。最后,与其说是错字更正不如将用户引导至login 路由的替代建议,您可以使用this.redirect()this.render() 函数而不是Router.go() 函数。有关可以为路由/路由控制器定义的所有可用选项的更多信息,请查看this out。

    现在是ProfileVerificationsController 定义:

        ProfileVerificationsController = ProfileController.extend({
            waitOn: function() {
                return Meteor.subscribe('userProfileVerification');
            },
            data: function() {
                if(this.ready()) {
                    var verifiedEmail = Meteor.user().emails && Meteor.user().emails[0].verified ? Meteor.user().emails[0].address : '';
                    var verifiedPhoneNumber = Meteor.user().customVerifications.phoneNumber && Meteor.user().customVerifications.phoneNumber.verified ? Meteor.user().customVerifications.phoneNumber.number : '';
    
                    var data = {
                        verifiedEmail: verifiedEmail,
                        verifiedPhoneNumber: verifiedPhoneNumber
                    };
                    return data;
                }
            }
        });
    

    请注意我更改的一件事,即使用if(this.ready()){} 包装控制器的data 选项中定义的所有代码。这在使用waitOn 选项时很重要,因为waitOn 选项将一个或多个订阅句柄添加到路由的等待列表中,并且this.ready() 仅当等待列表中的所有句柄都准备好时才返回true。确保使用此检查将防止在为路由构建数据上下文时意外未加载数据的任何情况。有关为您的路由/路由控制器定义订阅的更多信息,请查看this out。

    作为最后的建议,对于 ProfileController 中的 onBeforeAction 选项定义,我建议将其移至其自己的全局挂钩中,如下所示:

        Router.onBeforeAction(function() {
            if(!Meteor.user()) {
                Router.go(Router.path('login'));
            } else {
                this.next();
            }
        });
    

    在全局挂钩中定义此检查可确保您不必担心将您的ProfileController 添加到所有路由中,以确保对所有路由都运行此检查。每次访问一条路线时,都会对每条路线进行检查。不过,这只是一个建议,因为您可能有理由不这样做。我只是想建议它,因为我确保为我为提高安全性而开发的每个 Meteor 应用程序都这样做。

    【讨论】:

    • 感谢您的详细回答。我会注意你提到的事情。如您所见,我对 Meteor 还有些陌生 :)。无论如何,我已经用 Github repo 和测试站点更新了这个问题。即使在更改之后,这些钩子仍然会运行两次。
    • 没问题,我完全明白。我已经查看了您的网站,并且看到了 waitOn 和 data 函数是如何在主页上执行两次的。您能否提供使用这些控制器的代码,特别是您的路由定义代码?如果看不到这一点,就很难确定逻辑被执行两次的确切原因。
    • 当然,我已经将 repo 放到了 Github 上。这是路由文件 - github.com/niranjans/testRouter/blob/master/testRouter/lib/…
    • 我很抱歉,我完全错过了你之前把那个链接放在那里。我之前也这么想过,但现在我确信这与您在路由控制器 data 选项中使用 Meteor.user() 的事实有关。 Meteor.user() 是一个反应式数据源,您可以阅读有关 here 的信息。基本上,路由逻辑最初在页面加载时运行一次,并在加载来自反应数据源的数据后再次运行。这就是您看到 undefined 错误的原因,因为在第一次加载时,客户端上还不存在数据。
    • 没错,这只是正常的 Meteor 行为,确实很常见。在 data 选项中,如果您正在等待任何订阅,请务必将所有代码包装在 if(this.ready()){} 语句中,就像您在此应用程序中一样。通过该检查,您可以确保您订阅的数据以及您的Meteor.user() 对象已被解析并加载到客户端。如果我帮助回答了您的问题,请不要忘记将我的答案标记为正确。我希望你在 Meteor 中享受你的未来,并继续享受更多关于它的知识! :-)
    猜你喜欢
    • 2014-03-27
    • 1970-01-01
    • 1970-01-01
    • 2016-06-03
    • 2015-10-16
    • 1970-01-01
    • 2016-06-22
    • 2015-08-22
    • 2015-05-24
    相关资源
    最近更新 更多