[英]How to render nested collections in Meteor?
Summary: 摘要:
Child categories nested inside of Parent Categories are not getting rendered in a Meteor template. 嵌套在“父类别”中的子类别不会在Meteor模板中呈现。
Details: 细节:
Consider a data model for 'Category' as such: 考虑“类别”的数据模型:
// Model Schema
Category {
idCategory : 20, (id of the category itself)
idCategoryParent : 0, (idCategory of our parent category)
defaultLabel : "Movies" (our label)
}
There are parent categories and child categories. 有父类别和子类别。 Parent categories have an idCategoryParent property value of 0. Child categories store the idCategory of their parents as their idCategoryParent property.
父类别的idCategoryParent属性值为0.子类别将其父项的idCategory存储为其idCategoryParent属性。 I'm trying to loop through a collection of these Categories and render them in the following way:
我正在尝试遍历这些类别的集合并以下列方式呈现它们:
<b>Movies</b> // parent category is in bold
<ul> // child categories are rendered as an unordered list
<li>Horror</li>
<li>Comedy</li>
<li>Action</li>
<li>Drama</li>
</ul>
<b>Music</b>
<ul>
<li>Rock</li>
<li>Classical</li>
<li>Ambient</li>
</ul>
However, this is what I actually get: 然而,这是我实际得到的:
<b>Movies</b>
<ul> // empty...
</ul>
<b>Music</b>
<ul>
</ul>
Source Code: 源代码:
// How we get the 'categories_parents' data
Template.content.categories_parents = function (){
/*
* Get all parent categories (categories with an idCategoryParent of 0)
*/
var parents = Categories.find({idCategoryParent:0});
var pCount = parents.count();
for (var i = 0; i < pCount; i++){
var pId = parents.db_objects[i]['idCategory'];
/*
* Get all child categories of the parent (categories with
* an idCategoryParent equal to value of parent category's idCategory).
*/
var children = Categories.find({idCategoryParent:pId});
var cCount = children.count();
/*
* Assign the child categories array as a property of the parent category
* so that we can access it easily in the template #each expression
*/
parents.db_objects[i]['children'] = children;
}
return parents;
}
// This is our template
<template name="content">
<h1>Categories</h1>
{{#each categories_parents}}
<b>{{defaultLabel}}</b><br />
<ul>
{{#each children}}
<li>{{defaultLabel}}</li>
{{/each}}
</ul>
{{/each}}
</template>
Other template configurations I have tried in troubleshooting: 我在故障排除中尝试过的其他模板配置:
{{#each children}}
<li>A Child Exists Here</li> // Even this never rendered... no children?
{{/each}}
Any clues as to why this is happening would be appreciated. 任何关于为什么会发生这种情况的线索将不胜感激。
Your model is kind of iffy... Consider 你的模型有点不对......考虑一下
{name:"Category name", parent:"_id of parent category"}
Okay, that's a lot simpler. 好的,这更简单了。 To create a category.
创建一个类别。
var moviesId = Categories.insert({name:"Movies"});
Categories.insert({name:"Horror",parent:moviesId});
That was easy enough. 这很容易。 Now, to render in a way that
{{#each}}
works: 现在,以
{{#each}}
工作方式呈现:
Template.categories.categories = function(parent) {
if (parent) {
return Categories.find({parent:parent}).fetch();
} else {
return Categories.find({parent:{$exists:false}});
}
}
You might be seeing where this is going... 你可能会看到这是怎么回事......
<template name="categories">
{{#each categories}}
<ul>{{name}}
{{#each categories _id}}
<li>{{name}}</li>
{{/each}}
</ul>
{{/each}}
</template>
Now I'm not sure if the {{#each}}
block helper can take a function argument when it calls another helper. 现在我不确定
{{#each}}
块帮助器在调用另一个帮助器时是否可以使用函数参数。 If it doesn't... 如果不...
Template.categories.categories = function() {
return Categories.find({parent:{$exists:false}}).map(function(parentCategory) {
return _.extend(parentCategory,
{children:Categories.find({parent:parentCategory._id}).fetch()});
});
}
That's a real doozy. 这真是太过分了。 It returns parent categories with a new "children" list property, that contains all the children categories.
它返回具有新“children”列表属性的父类别,该属性包含所有子类别。 Now you can do:
现在你可以这样做:
<template name="categories">
{{#each categories}}
<ul>{{name}}
{{#each children}}
<li>{{name}}</li>
{{/each}}
</ul>
{{/each}}
</template>
Clever, eh? 聪明,是吗?
I don't know about db_objects, when I try and access that property on a cursor (which is what find()
returns), it's null
. 我不知道db_objects,当我尝试在游标上访问该属性时(这是
find()
返回的),它是null
。
You could fetch the items that matches your query instead, and then do your iteration: 您可以获取与您的查询匹配的项目,然后进行迭代:
Template.content.categories_parents = function (){
var parents = Categories.find({idCategoryParent:0}).fetch(); // Returns an array.
for (var i = 0; i < parents.length; i++){
var pId = parents[i]['idCategory'];
var children = Categories.find({idCategoryParent:pId});
// No need for array here, cursor is fine.
parents.db_objects[i]['children'] = children;
}
return parents;
}
I'm new at this myself, so maybe there's a more efficient way of doing it, but I don't know it currently. 我自己也是新手,所以也许有一种更有效的方法,但我目前还不知道。
Update after Eric's comment. Eric发表评论后更新。
The js file looks like this: js文件如下所示:
Categories = new Meteor.Collection("categories");
if (Meteor.isClient) {
Template.categories.categories = function () {
var parents = Categories.find({idCategoryParent:0}).fetch();
for (var i = 0; i < parents.length; i += 1) {
var pId = parents[i]['idCategory'];
var children = Categories.find({idCategoryParent:pId});
parents[i]['children'] = children;
}
return parents;
};
}
if (Meteor.isServer) {
Meteor.startup(function () {
Categories.remove({});
var data = [
{idCategoryParent: 0, idCategory: 1, label: "Movies"},
{idCategoryParent: 1, idCategory: 0, label: "Science Fiction"},
{idCategoryParent: 1, idCategory: 0, label: "Drama"},
{idCategoryParent: 0, idCategory: 2, label: "Music"},
{idCategoryParent: 2, idCategory: 0, label: "Jazz"},
{idCategoryParent: 2, idCategory: 0, label: "Piano"}
];
for (var i = 0; i < data.length; i += 1) {
Categories.insert(data[i]);
}
});
}
The html file looks like this: html文件如下所示:
<head>
<title>nested_template</title>
</head>
<body>
{{> categories}}
</body>
<template name="categories">
<h1>Categories</h1>
{{#each categories}}
<b>{{label}}</b>
<ul>
{{#each children}}
<li>{{label}}</li>
{{/each}}
</ul>
{{/each}}
</template>
It works just fine for me. 它对我来说很好。
Solved. 解决了。
My solution was to remove the {{#each}} logic from the template and replace it with a single handlebars helper expression, and pass back the needed html all at once. 我的解决方案是从模板中删除{{#each}}逻辑并将其替换为单个把手助手表达式,并立即传回所需的html。 The html is generated from data in the cursor of the Categories collection.
html是从Categories集合的游标中的数据生成的。
Not so sure about having all this html in my logic though -- is this bad design? 不太确定在我的逻辑中拥有所有这些HTML - 这是一个糟糕的设计吗? If so I'll defer to a better answer.
如果是这样,我会推迟一个更好的答案。
// This is the new template
<template name="content">
<h1>Categories</h1>
{{listCategories}}
</template>
// Handlebars helper
Handlebars.registerHelper('listCategories', function() {
var parents = Categories.find({idCategoryParent:0});
var countParents = parents.count();
var string = '';
// iterate over each parent category
for(m = 0; m < countParents; m++){
// Get the parents category id
var pId = parents.db_objects[m].idCategory;
var children = Categories.find({idCategoryParent:pId});
var count = children.count();
/*
* Build the Parent Category html
* Example: <b>Movies</b><ul>
*/
string = string + '<b>' + parents.db_objects[m].defaultLabel + '</b><ul>';
// iterate over each child category
for(var i = 0; i < count; i++){
/*
* Build the child category html
* Example: <li>Horror</li>
*/
string = string + '<li>' + children.db_objects[i]['defaultLabel'] + '</li>';
}
// Close up the unordered list
string = string + '</ul>';
}
// Return the string as raw html
return new Handlebars.SafeString(string);
});
// Rendered out the result correctly like so:
<b>Movies</b>
<ul>
<li>Horror</li>
<li>Comedy</li>
<li>Action</li>
<li>Drama</li>
</ul>
<b>Music</b>
<ul>
<li>Rock</li>
<li>Classical</li>
<li>Ambient</li>
</ul>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.