meteorspacebars

how to display objects in an array with spacebars


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})
} })

Solution

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

    {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}

    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.

    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.

    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}}