0👍
You can have multiple tbody
tags in a table. Wrap risk-component
in a tbody
and you can have multiple tr
s in it. You will need to do a little restructuring so that the tbody
s don’t nest.
var risks = '[{"title":"Risk1", "controls":[{"title":"Control1"}, {"title":"Control2"}, {"title":"Control3"}]},' +
'{"title":"Risk2", "controls":[{"title":"Control1"}, {"title":"Control2"}, {"title":"Control3"}]},' +
'{"title":"Risk3", "controls":[{"title":"Control1"}, {"title":"Control2"}, {"title":"Control3"}]}]';
var programs = '[{"title":"Program1", "practice":"IT", "auditType":"GAT", "version":"0.01", "programId":"2017.1", "status":"draft", "risks":' + risks + '},' +
'{"title":"Program2", "practice":"IT", "auditType":"On-request", "version":"0.01", "programId":"2017.2", "status":"draft", "risks":""},' +
'{"title":"Program3", "practice":"CA", "auditType":"GAT", "version":"0.01", "programId":"2017.3", "status":"approved", "risks":' + risks + '},' +
'{"title":"Program4", "practice":"CA", "auditType":"On-request", "version":"0.01", "programId":"2018.1", "status":"draft", "risks":' + risks + '},' +
'{"title":"Program5", "practice":"OA\FA", "auditType":"GAT", "version":"0.01", "programId":"2019.1", "status":"draft", "risks":' + risks + '},' +
'{"title":"Program6", "practice":"OA\FA", "auditType":"On-request", "version":"0.01", "programId":"2020.1", "status":"approved", "risks":' + risks + '}]';
var dataO = JSON.parse(programs);
Vue.component('control-component', {
props: ['control'],
template: '<tr class="control" >' +
'<td></td>' +
'<td colspan="6"><a href="#">{{ control.title }}</a></td>' +
'</tr>'
})
Vue.component('risk-component', {
props: ['risk'],
template: '<tbody><tr class="risk" >' +
'<td></td>' +
'<td colspan="5"><a href="#">{{risk.title}}</a></td>' +
'<td><span class="signrisk"></span></td>' +
'</tr>' +
'<control-component v-for="control in risk.controls" :control="control" :key="control.title"></control-component>' +
'</tbody>'
});
var programTable = new Vue({
el: '#programTable',
data: {
programs: ''
}
})
programTable.programs = dataO;
.signprogram:after {
content: "+";
display: inline-block;
cursor: pointer;
}
.expandprogram:after {
content: "-";
cursor: pointer;
}
.signrisk:after {
content: "+";
display: inline-block;
cursor: pointer;
}
.expandrisk:after {
content: "-";
cursor: pointer;
}
th {
background-color: #e0e0e0;
}
.program {
background-color: #e9e9e9;
}
.risk {
background-color: #eeeeee;
}
.control {
background-color: #f2f2f2;
}
.spacing {
background-color: white;
}
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<table class="table" width="300px" id="programTable">
<thead>
<tr>
<th>Status</th>
<th>Title</th>
<th>Practice</th>
<th>Audit Type</th>
<th>Version</th>
<th>Program</th>
<th> </th>
</tr>
</thead>
<template v-for="program in programs">
<tbody>
<tr class="program" >
<td v-if="program.status == 'draft'" style="width: 20px; background-color: lightblue;"> </td>
<td v-if="program.status == 'approved'" style="width: 20px; background-color: lightgreen;"> </td>
<td><a href="/auditprograms/1">{{program.title}}</a></td>
<td>{{program.practice}}</td>
<td>{{program.auditType}}</td>
<td>{{program.version}}</td>
<td>{{program.programId}}</td>
<td><span class="signprogram"></span></td>
</tr>
</tbody>
<risk-component v-for="risk in program.risks" :risk="risk" :key="program.title + risk.title"></risk-component>
</template>
</table>
0👍
For a similar question I purpose an other answer : Vue js error: Component template should contain exactly one root element
In your case you have to delegate the <tbody/>
to a functionnal component tha will construct a list of <tr/>
components.
Or to delegate the <table/>
to a functionnal component tha will construct a list of <tbody/>
components.
Or both.
copy/paste here :
if, for any reasons, you don’t want to add a wrapper (in my first case it was for <tr/>
components), you can use a functionnal component.
Instead of having a single components/MyCompo.vue
you will have few files in a components/MyCompo
folder :
components/MyCompo/index.js
components/MyCompo/File.vue
components/MyCompo/Avatar.vue
With this structure, the way you call your component won’t change.
components/MyCompo/index.js
file content :
import File from './File';
import Avatar from './Avatar';
const commonSort=(a,b)=>b-a;
export default {
functional: true,
name: 'MyCompo',
props: [ 'someProp', 'plopProp' ],
render(createElement, context) {
return [
createElement( File, { props: Object.assign({light: true, sort: commonSort},context.props) } ),
createElement( Avatar, { props: Object.assign({light: false, sort: commonSort},context.props) } )
];
}
};
And if you have some function or data used in both templates, passed them as properties and that’s it !
I let you imagine building list of components and so much features with this pattern.
- [Vuejs]-Add Scrollbar on Bootstrap table grid under col 12 div
- [Vuejs]-How To use Vue.js template in another HTML file