我有一个应该显示一些数据的Meteor模板。
Template.svg_template.rendered = function () {
dataset_collection = Pushups.find({},{fields: { date:1, data:1 }}, {sort: {date: -1}}).fetch();
a = moment(dataset_collection[0].date, "YYYY/M/D");
//more code follows that is also dependent on the collection being completely loaded
};
有时可行,有时我会收到此错误:
Deps afterFlush函数的异常:TypeError:无法读取未定义的属性'date'
我在任何情况下都不使用Deps。据我了解,该集合在完成加载之前就被引用了。
因此,我想弄清楚如何简单地说“等到收集之后才能继续前进”。应该是直截了当的,但找不到更新的解决方案。
答案 0 :(得分:32)
你是对的,你应该确保在正确加载数据之后执行取决于获取客户端订阅集合内容的代码。
您可以使用Meteor 1.0.4中引入的新模式实现此目的:https://docs.meteor.com/#/full/Blaze-TemplateInstance-subscribe
client/views/svg/svg.js
Template.outer.onCreated(function(){
// subscribe to the publication responsible for sending the Pushups
// documents down to the client
this.subscribe("pushupsPub");
});
client/views/svg/svg.html
<template name="outer">
{{#if Template.subscriptionsReady}}
{{> svgTemplate}}
{{else}}
Loading...
{{/if}}
</template>
在Spacebars模板声明中,我们使用封装outer
模板来处理模板级订阅模式。
我们在onCreated
生命周期事件中订阅了该发布,并且我们使用特殊的反应助手Template.subscriptionsReady
仅在订阅准备就绪后呈现svgTemplate
(数据在浏览器中可用)。
此时,我们可以安全地查询Pushups
svgTemplate
生命周期事件中的onRendered
集合,因为我们确保数据已经发送到客户端:
Template.svgTemplate.onRendered(function(){
console.log(Pushups.find().fetch());
});
或者,您可以使用iron:router
(https://github.com/iron-meteor/iron-router),它提供了另一种设计模式来实现此常见的Meteor相关问题,在路由级别而非模板级别移动订阅处理。
将包添加到您的项目中:
meteor add iron:router
lib/router.js
Router.route("/svg", {
name: "svg",
template: "svgTemplate",
waitOn: function(){
// waitOn makes sure that this publication is ready before rendering your template
return Meteor.subscribe("publication");
},
data: function(){
// this will be used as the current data context in your template
return Pushups.find(/*...*/);
}
});
使用这段简单的代码,您将得到您想要的内容以及许多附加功能。 您可以查看Iron Router指南,其中详细介绍了这些功能。
https://github.com/iron-meteor/iron-router/blob/devel/Guide.md
2015年5月18日编辑:重新修改了答案,因为它包含过时的材料,但仍然收到了赞成票。
答案 1 :(得分:3)
这是我真正希望直接解决基本流星文档的问题之一。这令人困惑,因为:
Deps
错误并未指出根本问题。因此,正如您已经想到的那样,在呈现模板时,您的数据尚未就绪。什么是最简单的解决方案?假设数据可能没有准备好。 examples做了很多这方面的工作。来自leaderboard.js:
Template.leaderboard.selected_name = function () {
var player = Players.findOne(Session.get("selected_player"));
return player && player.name;
};
只有实际找到player
时,才会访问player.name
。在coffeescript中,你可以使用浸泡来完成同样的事情。
waitOn
的建议对这个特定用例有好处,但请注意,您很可能遇到应用中的情况,其中数据不存在于数据库或所需属性在获取的对象上不存在。
不幸的是,在许多情况下,必须采取一些防御性编程。
答案 2 :(得分:0)
使用iron-router等待订阅工作,但我喜欢将集合管理的集合保存在collections.js文件中。相反,我利用Meteor的file load order在其他所有内容之前加载订阅。
这是我的collections.js文件的样子:
// ****************************** Collections **********************************
Groups = new Mongo.Collection("groups");
// ****************************** Methods **************************************
myGroups = function (userId) {
return Groups.find({"members":{$elemMatch:{"user_id":userId}}});
};
// ****************************** Subscriptions ********************************
if(Meteor.isClient){
Meteor.subscribe("groups");
}
// ****************************** Publications *********************************
if(Meteor.isServer){
Meteor.publish("groups", function () {
return myGroups(this.userId);
});
}
然后我将collections.js放入lib/
文件夹,以便在我的典型客户端代码之前加载它。这样,订阅将集中到单个collections.js文件中,而不是作为我的路由的一部分。此示例还集中了我的查询,因此客户端代码可以使用相同的方法来提取数据:
var groups = myGroups(Meteor.userId());