I am using v-for in Nuxt vue2 to create a table with a table with in it but cant find any way to target the second table.
This is the HTML I am using and the command lines I am using to target the data.
The first table appears to work but I have tried everything I can think of to target the second table. Hopefully someone can show me how to do it and more importantly explain why there is a problem with the inner table.
<template>
<div>
Working with the DOM Vue2
<table ref="table1">
<tr v-for="index in 5" :key="index">
<th>Company{{ index }}</th>
<th>
<table ref="tableChild">
<tr v-for="index in 2" :key="index">
<th>Inner table Line{{ index }}</th>
</tr>
</table>
</th>
</tr>
</table>
</div>
</template>
<script>
export default {
mounted() {
//-------- targeting the two tables --------------
console.log("table1:", this.$refs.table1);
console.log("table1-all children:", this.$refs["table1"].children);
console.log("table1-all children0:", this.$refs["table1"].children[0]);
console.log("tableChild:", this.$refs.tableChild);
console.log("tableChild-all children:", this.$refs["tableChild"].children);
console.log("tableChild-all children0:", this.$refs["tableChild"].children[0]);
}
}
</script>
<style>
</style>
To access the inner table data by using ref. Here you go :
this.$refs.tableChild[0].children[0]
Live Demo :
new Vue({
el: '#app',
mounted() {
console.log("table1:", this.$refs.tableChild[0].children[0].innerText);
}
})
table, tr, th {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table ref="table1">
<tr v-for="index in 5" :key="index">
<th>Company{{ index }}</th>
<th>
<table ref="tableChild">
<tr v-for="index in 2" :key="index">
<th>Inner table Line{{ index }}</th>
</tr>
</table>
</th>
</tr>
</table>
</div>
Related
When trying to render a component, the template string is rendering not where the component is defined in the HTML. Instead, the rendered HTML is getting rendered outside of my <table>.
Easiest way to describe this is with the code snippet below. Column 2 is appearing outside the <table> altogether, even tho I have <my-th></my-th> directly after my <th>Column 1</th>.
Why is this happening?
Vue.component('my-th', {
props: [],
template: `<th>Column 2</th>`
});
new Vue({
el: "#app"
});
body { background: white; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<thead>
<tr>
<th>Column 1</th>
<my-th></my-th><!-- Column 2 -->
</tr>
</thead>
<tbody>
<tr>
<td>data 1</td>
<td>data 2</td>
</tr>
</tbody>
</table>
</div>
You need to use Vue's special is attribute in this scenario.
The browser will parse the HTML before Vue gets anywhere near it and any elements inside a table that aren't where they're supposed to be will be moved out. The browser doesn't know what a my-th is so it moves it out to before the table.
This is specifically a problem with templates defined in HTML where it will be parsed by the browser first. For the various other ways to specify a template it isn't an issue as Vue can see the template in its original form.
See https://v2.vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats for details.
Vue.component('my-th', {
props: [],
template: `<th>Column 2</th>`
});
new Vue({
el: "#app"
});
body { background: white; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<thead>
<tr>
<th>Column 1</th>
<th is="my-th"></th><!-- Column 2 -->
</tr>
</thead>
<tbody>
<tr>
<td>data 1</td>
<td>data 2</td>
</tr>
</tbody>
</table>
</div>
Vue 2, no webpack. I want to render two trs at a time, for main and detail expandable row. This is what I'm trying to achieve:
<table>
<tbody>
<div v-for="item in items">
<tr></tr>
<tr class="detail-row"></tr>
</div>
</tbody>
</table>
The problem is that <div> is an invalid child of tbody. How to render two <tr>s at each for loop iteration?
This is the way you solve it in browsers that support template.
<table>
<tbody>
<template v-for="item in items">
<tr></tr>
<tr class="detail-row"></tr>
</template>
</tbody>
</table>
If you need to support browsers that do not support template, I typically resort to a render function.
Here is a working example of both.
console.clear()
new Vue({
el: "#app",
data: {
items: [{
master: "Master",
detail: "Detail"
},
{
master: "Master",
detail: "Detail"
},
]
}
})
new Vue({
el: "#app2",
data: {
items: [{
master: "Master",
detail: "Detail"
},
{
master: "Master",
detail: "Detail"
},
]
},
render(h){
// build the rows
let rows = []
for (let item of this.items){
rows.push(h("tr", [h("td", [item.master])]))
rows.push(h("tr", {class: "detail-row"}, [h("td", [item.detail])]))
}
// add rows to body
let body = h("tbody", rows)
// return the table
return h("table", [body])
}
})
.detail-row{
background-color: lightgray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<h2>Using template</h2>
<div id="app">
<table>
<tbody>
<template v-for="item in items">
<tr><td>{{item.master}}</td></tr>
<tr class="detail-row"><td>{{item.detail}}</td></tr>
</template>
</tbody>
</table>
</div>
<h2>Using render function</h2>
<div id="app2"></div>
In newer version of VueJS, it wants an index. So the solution would look like
<table>
<tbody>
<template v-for="(item, index) in items">
<tr :key="index">
<td>{{item.master}}</td>
</tr>
<tr :key="index" class="detail-row">
<td>{{item.detail}}</td>
</tr>
</template>
</tbody>
</table>
If you want to use in double tag.
Or want to use a separate component in the template div within the table tr tags (as in a new component) you could use style="display: contents" in the first div to keep the table rows inline with each other.
Vue component
<table>
<template v-for="v-for="(price, index) in prices">
<div :key="price.id" style="display: contents">
<tr><td>{{price.id}}</td><td>{{price.name}}</td></tr>
<tr col-span="2">{{price.desc}}</tr>
</div>
</template>
</table>
Or if you want to use a separate component for the rows
Table.vue
<template>
<div>
<table class="table">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
</tr>
</thead>
<tbody>
<template v-for="item in items">
<my-component :item=“item”/>
</template>
</tbody>
</table>
</div>
</template>
my-component.vue
<template>
<div style="display: contents">
<tr>
<td>{{item.firstname}}</td>
<td>{{item.lastname}}</td>
</tr>
<tr>
<td colspan="2" >
{{item.description}}
</td>
</tr>
</div>
</template>
Instead of using display: content in the div and you want the rows linked together you can use display: table-row-group type
<table>
<div v-for="v-for="(price, index) in prices"
v-bind:key="price.id"
style="display: table-row-group">
<tr>
<td>{{price.id}}</td><td>{{price.name}}</td>
</tr>
<tr col-span="2">
{{price.desc}}
</tr>
</div>
</table>
I would like to store loop object in id of HTML element so that I can use it when click on this element. My code is like below
<tr v-for="obj in data">
<td v-for="(value, key) in obj" :id="obj"></td>
</tr>
But it is not working. Could anyone help me in this regard ?
You Can Call Method On Click Of td element of table , and pass current obj to that method.
var app = new Vue({
el: '#app',
data: {
arr:[{name:'Demo User',email:'demoUser#gmail.com'}, {name:'James Bond',email:'james#gmail.com'}]
},
methods:{
getData:function(item){
alert(item.name+"--"+item.email);
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<table border="1">
<tbody>
<tr v-for="item in arr">
<td v-for="(value, key) in item" v-on:click="getData(item)">{{value}}</td>
</tr>
</tbody>
</table>
</div>
I am simply trying to render a list in my view and I keep seeing the error:
[Vue warn]: Property or method "client" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
Here's my view:
<div class="col-xs-6 col-xs-offset-3 text-center" id="vueapp">
<div class="table table-awaken" v-if="clients">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr v-for="client in clients">
<td>{{ client }}</td>
</tr>
</tbody>
</div>
</div>
and my vue instance:
var vote = new Vue({
el: "#vueapp",
data: {
clients: [
{ name: 'x' }
]
}
})
It is because you have thead and tbody that are not children of a table element. Browsers do interesting things when you break HTML rules like that.
See these docs which state that tbody and thead must be children of a table.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead
If you wrap those elements in a table element, it begins working. I am not sure if this is because of Vue or the browser.
Try this HTML instead:
<div class="col-xs-6 col-xs-offset-3 text-center" id="vueapp">
<div class="table table-awaken">
<table>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr v-for="client in clients">
<td>{{ client }}</td>
</tr>
</tbody>
</table>
</div>
</div>
And here is a working jsfiddle: https://jsfiddle.net/kg638x4f/
I am trying to render a table. The data is dynamic and comes from an array. It works fine, except: the table content is rendered outside of the table on top of the page.
I would like it to render inside the table. Would anyone be able to help?
This what the code looks like:
Vue.js:
Vue.component('word', {
props: ['word-list'],
template: '#word-template'
});
new Vue({
el: '#root'
});
HTML:
<div id="root">
<word :word-list="{{json_encode($commonWords) }}"></word>
<template id="word-template">
<table class="table">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr v-for="(value, key) in wordList" :wordList="wordList">
<td> #{{ key }} </td>
<td> #{{ value }} </td>
</tr>
</tbody>
</table>
</template>
</div>
Note: This is used with Laravel, thats why there is an # before double curly braces.
You can't define the template for your component inside the template for your Vue. You need to move it outside.
<div id="root">
<word :word-list="{{json_encode($commonWords) }}"></word>
</div>
<template id="word-template">
<table class="table">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr v-for="(value, key) in wordList" :wordList="wordList">
<td> #{{ key }} </td>
<td> #{{ value }} </td>
</tr>
</tbody>
</table>
</template>
If you leave the template for the component inside the template for the root, the root will compile the template for the component as part of it's own template.