user2402616
user2402616

Reputation: 1563

Iterate over Objects in Meteor Blaze

I have a data structure that is a nested object. It’s a map that has unique id keys.

Each key then contains another map which consists of another set of unique id keys (_id corresponding to Mongo documents). And each one of these keys contains a Mongo Document.

https://i.sstatic.net/1IeTJ.jpg is an example of how the data structure looks like in Chrome dev tools.

I want to iterate over this in Blaze. My goal is to have a header that displays the top-level keys (1 & 2 in this example)…and then for each top level-key, have a table where each row is a nested document.

I understand that Blaze can only iterate through arrays and cursors and not JS Objects. https://github.com/meteor/meteor/issues/3884 And so I used _.pair to serialize my nested object into an array and here's my result https://i.sstatic.net/qojXj.jpg

I’m having trouble getting what I want with the 2D array though. Because now my top-level key (1 & 2 in this example) are at the first index of an array. I can’t figure out how to access this inside an each.

Here’s my full goal in a nutshell I.e.

{{#each 2dArray}}
    print {{this[0]}}
    <table>
        {{#each this[1]}}
            <tr>{{nestedThis.data}}</tr>
        {{/each}}
   </table>
{{/each}}

Does anybody know how to do this or is there a better way to structure my data too? Thanks

Upvotes: 0

Views: 363

Answers (1)

wildhart
wildhart

Reputation: 449

You should be able to restructure your data as follows

input = {
 1: {
   bob001: {_id: "bob001", name: "Bob" },
   jim002: {_id: "jim002", name: "Jim" },
 },
 2: { 
   kim001: {_id: "kim001", name: "Kim" },
   sue002: {_id: "sue002", name: "Sue" },
 }
}
 
 console.log(input);
 
 output = Object.keys(input).map(key => {
   return {
     key: key,
     data: Object.values(input[key]).map(row => {
       return Object.keys(row).map(k => {
         return {key: k, data: row[k]};
       });
     })
   }
})
 
console.log(output);

In your component, restructure the input into a new ReactiveVar. Do this in an $autorun, assuming the input is reactive (thanks @Jankapunkt):

Template.my_template.onCreated({
  this.formattedData = new ReactiveVar([]);
  this.$autorun(() => {
    const input = this.input; // get this from somewhere reactive?...
    this.formattedData = Object.keys(input).map(key => {
      return {
        key: key,
        data: Object.values(input[key]).map(row => {
          return Object.keys(row).map(k => {
            return {key: k, data: row[k]};
          });
        })
      }
    })
  })
})

Then you can display it using blaze:

  {{#each formattedData}}
    print {{key}}
    <table>
      {{#each data}}
        <tr>
        {{#each this}}
          <td>{{key}}: {{data}}</td>
        {{/each}}
        </tr>
      {{/each}}
    </table>
  {{/each}}

Upvotes: 2

Related Questions