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>