3

I can I loop through multi-nested object collection while still displaying in the same table?

<table v-for="d in transaction.documents">
    <tbody>
        <tr>
            <th>Document ID:</th>
            <td>{{ d.id }}</td>
        </tr>
    </tbody>
    <tbody v-for="t in d.tasks">
        <tr>
            <th>Task ID:</th>
            <td>{{t.id}}</td>
        </tr>
    </tbody>
    <tbody v-for="a in t.actions">  <!-- t is no longer available because it's not still in the same <tbody> -->
        <tr>
            <th>Action ID:</th>
            <td>{{ a.id) }}</td>
        </tr>
    </tbody>
</table>

I need to be doing something along these lines but this is invalid HTML.

<table v-for="d in transaction.documents">
    <tbody>
        <tr>
            <th>Document ID:</th>
            <td>{{ d.id }}</td>
        </tr>
    </tbody>
    <tbody v-for="t in d.tasks">
        <tr>
            <th>Task ID:</th>
            <td>{{t.id}}</td>
        </tr>
        <tbody v-for="a in t.actions">
            <tr>
                <th>Action ID:</th>
                <td>{{ a.id) }}</td>
            </tr>
        </tbody>
    </tbody>
</table>
3
  • 1
    Can you clarify what you want the table to show? What are the columns and what are the rows? Commented Jul 29, 2018 at 20:38
  • 1
    Use a render function. Commented Jul 29, 2018 at 20:53
  • Thanks, @Bert - this is exactly what I was looking for! Commented Jul 30, 2018 at 13:42

3 Answers 3

5

You can achieve this like below using nester v-for and the <template> tag :

<table v-for="d in transaction.documents">
    <tbody>
        <tr>
            <th>Document ID:</th>
            <td>{{ d.id }}</td>
        </tr>
    </tbody>
    <tbody v-for="t in d.tasks">
        <tr>
            <th>Task ID:</th>
            <td>{{t.id}}</td>
        </tr>
    </tbody>
    <template v-for="t in d.tasks">  <!-- This tag won't display but help you to nest two foreach -->
        <tbody v-for="a in t.actions">
            <tr>
                <th>Action ID:</th>
                <td>{{ a.id) }}</td>
            </tr>
        </tbody>
    </template>
</table>
Sign up to request clarification or add additional context in comments.

Comments

1

I wanted to add a more complete answer than my comment above. Essentially I can think of two strategies for rendering the data the way you want.

First, whenever your data is not in a structure you want, or a structure that is easily worked with, you can always build a new data structure using a computed property that derives from the original structure. This is probably the easiest approach.

Here, for example is a computed property that re-formats your data into a structure that can be easily iterated.

tables(){
  const tables = []
  for (const document of this.transaction.documents){
    const rows = []
    for (const task of document.tasks){
      rows.push({header: "Task ID", id: task.id})
      for (const action of task.actions){
        rows.push({header: "Action ID", id: action.id})
      }
    }
    tables.push({header: "Document ID", id: document.id, rows})
  }
  return tables
}

Which means that you could use simple loops in your template to render your data.

<div id="app">
  <table v-for="table in tables">
    <tr><th>{{table.header}}</th><td>{{table.id}}</td><tr></tr>
    <tr v-for="row in table.rows">
      <th>{{row.header}}</th>
      <td>{{row.id}}</td>
    </tr>
  </table>
</div>

Here is an example of that in action.

console.clear()

new Vue({
  el: "#app",
  data:{
    transaction: {
      documents:[
        {
          id: 1,
          tasks:[
            {
              id: 1,
              actions:[
                {id: 1},
                {id: 2},
                {id: 3}

              ]
            },
            {
              id: 2,
              actions:[
                {id: 4}
              ]
            }
            
          ]
        }
      ]
    }
  },
  computed:{
    tables(){
      const tables = []
      for (const document of this.transaction.documents){
        const rows = []
        for (const task of document.tasks){
          rows.push({header: "Task ID", id: task.id})
          for (const action of task.actions){
            rows.push({header: "Action ID", id: action.id})
          }
        }
        tables.push({header: "Document ID", id: document.id, rows})
      }
      return tables
    }
  },
})
th {
  text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
  <table v-for="table in tables">
    <tr><th>{{table.header}}</th><td>{{table.id}}</td><tr></tr>
    <tr v-for="row in table.rows">
      <th>{{row.header}}</th>
      <td>{{row.id}}</td>
    </tr>
  </table>
</div>

Second, you could use a render function. Render functions give you all the flexibility of javascript in deciding how to render your template. Here is the render function I came up with in the comment above.

methods:{
  buildTable(h, document){
    const rows = []
    // build and add the document row
    const documentRow = h("tr", [h("th", "Document ID"), h("td", document.id)])
    rows.push(documentRow)

    // build the task rows
    for (const task of document.tasks){
      const taskRow = h("tr", [h("th", "Task ID"), h("td", task.id)])
      rows.push(taskRow)

      //build the action rows
      for (const action of task.actions){
        const actionRow = h("tr", [h("th", "Action ID"), h("td", action.id)])
        rows.push(actionRow)
      }
    }

    return rows
  }
},
render(h){
  const tables = []
  for (const document of this.transaction.documents)
    tables.push(h("table", this.buildTable(h, document)))

  return h("div", tables)
}

And here is an example of that in action.

console.clear()

new Vue({
  el: "#app",
  data:{
    transaction: {
      documents:[
        {
          id: 1,
          tasks:[
            {
              id: 1,
              actions:[
                {id: 1},
                {id: 2},
                {id: 3}

              ]
            },
            {
              id: 2,
              actions:[
                {id: 4}
              ]
            }
            
          ]
        }
      ]
    }
  },
  methods:{
    buildTable(h, document){
      const rows = []
      // build and add the document row
      const documentRow = h("tr", [h("th", "Document ID"), h("td", document.id)])
      rows.push(documentRow)
      
      // build the task rows
      for (const task of document.tasks){
        const taskRow = h("tr", [h("th", "Task ID"), h("td", task.id)])
        rows.push(taskRow)
        
        //build the action rows
        for (const action of task.actions){
          const actionRow = h("tr", [h("th", "Action ID"), h("td", action.id)])
          rows.push(actionRow)
        }
      }
      
      return rows
    }
  },
  render(h){
    const tables = []
    for (const document of this.transaction.documents)
      tables.push(h("table", this.buildTable(h, document)))
    
    return h("div", tables)
  }
})
th {
  text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app"></div>

Comments

1

What you are doing wrong is placing multiple tbody inside one table and th inside tbody instead of thead.

You might be looking for something like this: http://jsfiddle.net/eywraw8t/218109/ You can replace ul and li parts with nested tables, but to be honest I've no idea how you want to make your table readable.

3 Comments

I don't think this is exactly what he wants (I think he wants everything to be a row) but voting up for taking the time to build a nice example.
This might be closer to what he wants then: jsfiddle.net/eywraw8t/218159 I'd leave actions as lists for simplicity. If he wants them to be table rows, he would probably have to map tasks to actions.
Sorry - @Bert is correct, I need everything as a normal table row and not indented in any way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.