简体   繁体   English

如何在Meteor中渲染嵌套集合?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM