0

This may be a duplicate question but I've yet to find a solution to my specific problem. I have a db set up like this:

{ "_id" : ObjectId("5c43b0e463ad7e8adfa4f07a"), "name" : "The Box", "price" : "80", "parts" : [ { "pname" : "piccolo", "pprice" : "3" }, { "pname" : "Flute 1", "pprice" : "3" } ] }

Is there a way to iterate through the parts array and do a nested {{#each}} loop so that I can display the name of each document and each part within the name? my code so far:

<tbody>
                    {{#each pieces}}
                    <tr class="itemList">
                        <td class="name">{{name}}</td>
                        <td class="pdf">PDF</td>
                        <td class="audio">AUDIO</td>
                        <td class="format">FORMAT</td>
                        <td class="price" >${{price}}</td>
                        <td><input class ="qty" type ="number" name ="quantity" value="0"></td>
                    </tr>
                    {{#each parts}}
                    <tr>
                        <td colspan="3"></td>
                        <td class="partName">{{pname}}</td>
                        <td class="partPrice">{{pprice}}</td>
                        <td><input class="partQty" type="number" name="quantity" value="0"></td>
                    </tr>
                    {{/each}}
                    {{/each}}
                </tbody>

and my helpers:

   Template.band.helpers({
pieces: function(){
    return bandmusic.find({})
},
parts: function(){
    return bandmusic.find({parts})
} })

2 Answers2

1

You don't have to create two helpers. Just using your first helper is working:

Template.hello.helpers({
  pieces() {
    let data = [{
      "_id": "5c43b0e463ad7e8adfa4f07a",
      "name": "The Box",
      "price": "80",
      "parts": [
        { "pname": "piccolo", "pprice": "3" },
        { "pname": "Flute 1", "pprice": "3" }
      ]
    }]

    return data;
  }
});

Then you can iterate on the pieces and on the parts. If you want to access inside your nested #each the piece name AND the part name. You can use {{../name}} to access the parent Datacontext.

<table class="table">
  <tbody>
    {{#each pieces}}
    <tr class="itemList">
      <td class="name">{{name}}</td>
      <td class="pdf">PDF</td>
      <td class="audio">AUDIO</td>
      <td class="format">FORMAT</td>
      <td class="price">${{price}}</td>
      <td><input class="qty" type="number" name="quantity" value="0"></td>
    </tr>
      {{#each parts}}
      <tr>
        <td colspan="3">{{../name}}</td>
        <td class="partName">{{pname}}</td>
        <td class="partPrice">{{pprice}}</td>
        <td><input class="partQty" type="number" name="quantity" value="0"></td>
      </tr>
      {{/each}}
    {{/each}}
  </tbody>
</table>

But using the {{#each}} block is not a good practice, you should use {{#each in}}. The {{#each in}} block create a variable that you can use easily.

<table class="table">
  <tbody>
    {{#each piece in pieces}}
    <tr class="itemList">
      <td class="name">{{piece.name}}</td>
      <td class="pdf">PDF</td>
      <td class="audio">AUDIO</td>
      <td class="format">FORMAT</td>
      <td class="price">${{piece.price}}</td>
      <td><input class="qty" type="number" name="quantity" value="0"></td>
    </tr>
      {{#each part in piece.parts}}
      <tr>
        <td colspan="3">{{piece.name}}</td>
        <td class="partName">{{part.pname}}</td>
        <td class="partPrice">{{part.pprice}}</td>
        <td><input class="partQty" type="number" name="quantity" value="0"></td>
      </tr>
      {{/each}}
    {{/each}}
  </tbody>
</table>
Gaëtan Rouziès
  • 489
  • 1
  • 3
  • 16
1

The first thing to realise is that your parts helper a) doesn't work and b) isn't what you need anyway.

  • a) bandmusic.find({parts}) doesn't mean "return the parts array from documents in the bandmusic collection", which is what you seem to be wanting it to do.

{parts} is ES6 shorthand for {parts: parts} (see this answer). But your function has no variable parts - so in your find, what you're really saying is "find documents which match the condition {parts: undefined}

  • b) But that doesn't matter, because you don't need a helper here in the first place.

What you're trying to do with your nested #each is loop through each document in the database, and then within each document, loop through the parts array.

Well, you get the documents from your pieces helper, and each document contains a parts array, which you can just loop through without needing a helper.

  • c) Simple solution

Your code should work if you just delete the parts helper. Blaze has a lookup order which you can read about here. What this means is that when Blaze sees parts, it first thinks "is there a helper called parts?" - which there is, and it's not working, so nothing happens.

But what you want it to think is "Is there a field in the current data context called parts" - which there is, but helpers come higher in the lookup order so it never gets there.

So the simplest solution in theory is to remove the helper.

  • d) Clearer solution and best practice

As you can see from the lookup order link, it's often unclear what refers to what in Spacebars/Blaze. You can make things much clearer by using the syntax described in the Blaze docs for each

Instead of #each array you should introduce a new variable to refer to the current item in the array - #each item in array. And then access the item's properties as usual - item.prop1 - item.prop2

So your new code becomes:

{{#each piece in pieces}}
  <tr class="itemList">
    <td class="name">{{piece.name}}</td>
    <td class="pdf">PDF</td>
    <td class="audio">AUDIO</td>
    <td class="format">FORMAT</td>
    <td class="price" >${{piece.price}}</td>
    <td><input class ="qty" type ="number" name ="quantity" value="0"></td>
  </tr>
  {{#each part in piece.parts}}
    <tr>
      <td colspan="3"></td>
      <td class="partName">{{part.pname}}</td>
      <td class="partPrice">{{part.pprice}}</td>
      <td><input class="partQty" type="number" name="quantity" value="0"></td>
    </tr>
  {{/each}}
{{/each}}
rubie
  • 1,028
  • 1
  • 14
  • 22
  • 1
    that makes a lot more sense. the each in context works a lot better and as soon as I altered my code it worked like a charm! Thank you for the clear explanation. – Corey Cerullo Jan 20 '19 at 13:33
  • So then could I do an {{#if}} conditional to check if that array exists? so lets say some of the documents in the db have a parts array and others don't. So would that be {{#if piece.parts}} execute the other {{#each..in}} loop? – Corey Cerullo Jan 20 '19 at 14:35
  • Yes, but much easier is {{#each...}}Some stuff {{else}} No Stuff Found {{/each}} - http://blazejs.org/api/spacebars.html#Each – rubie Jan 20 '19 at 14:39