VueJs component not being replaced with the template - vue.js

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>

Related

How do I target a dynamic second table in vue2

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>

How to use <th> as slot in vue.js

Good evening, everyone,
I have a vue-template which should generate a table. The table header, more precisely the number of individual column headers can be determined dynamically via the "slots".
Unfortunately there is always the error that there can only be one root element.
How can I - as easy as possible - make sure that my components are inserted into the header anyway.
table.vue
<template>
<table>
<thead>
<tr>
<slot></slot>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
</tr>
</tbody>
</table>
</template>
And this ist how i would like to use it:
<lbtable>
<th>Column 1</th>
<th>Column 2</th>
</lbtable>
Here the error-message: Cannot use slot as component root element because it may contain multiple nodes.
Edit:
Here you can find the example:
https://codesandbox.io/s/small-waterfall-k6fit
Try this! You just need to wrap <th> with <template> tag.
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<lbtable>
<template>
<th>Column 1</th>
<th>Column 2</th>
</template>
</lbtable>
</div>
</body>
</html>
<script>
Vue.component('lbtable', {
'template': `
<div>
<table border="1">
<thead>
<tr>
<slot></slot>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
</tr>
</tbody>
</table>
</div>`});
new Vue({
el: "#app"
});
</script>

Vue component and datatypes props

I'm trying to create a vue component to all my datagrids, I'm using DataTables library (https://datatables.net/), so, i've already create my vue component:
GridDatos.vue
<template>
<div class="container">
<h4>{{titulo}}</h4>
<table id="example" class="table table-striped table-bordered nowrap" style="width:100%">
<thead>
<tr>
<th v-for="col in colNames">{{col}}</th>
</tr>
</thead>
<tbody>
<tr>
<td>sssss</td>
<td>sssss</td>
<td>sssss</td>
<td>sssss</td>
<td>sssss</td>
<td>sssss</td>
</tr>
</tbody>
<tfoot>
<tr>
<th v-for="col in colNames">{{col}}</th>
</tr>
</tfoot>
</table>
</div>
The instance:
Vue.component('grid-datos', require('./components/GridDatos.vue').default);
And the usage:
<div id="app">
<grid-datos
url="/api/listadoContactos"
titulo="Mis Datos"
:col-names="('Name','Position','Office','Age','Start date','Salary')">
</grid-datos>
</div>
But the problem here is the table header, it seems to use the last array element as you can see in the screenshot:
Titles
Definitely i'm not setting the array correctly when call the component.
Could anybody help me?
Thanks a lot. Regards.

Vue: List rendering returns error

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/

vueJS hide element in dynamic table row

I have a table that looks like this:
<table class="table">
<thead>
<tr>
<td><strong>Title</strong></td>
<td><strong>Job</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows">
<td><p class="title">My title</p></td>
<td><input type="text" v-model="row.job"></td>
<td><a #click="title()">Remove title</a></td>
</tr>
</tbody>
</table>
Now I wonder how I can toggle a jquery .hide() on the <p class="title">My title</p> when the remove title link is clicked on.
I dont want to use v-show since I am trying to understand how I can target elements within dynamic generated rows in vueJS.
The problem is that there are many rows in my table so every title tag must have a uniqe class and I dont understand how I can hide a specific title on dynamic generated rows
Can be done this way using v-show directive.
new Vue({
el: '.table',
data: {
rows: [
{ showTitle: true, job: 'A' },
{ showTitle: true, job: 'B' },
{ showTitle: true, job: 'C' }
]
}
});
<script src="https://unpkg.com/vue#2.5.2/dist/vue.min.js"></script>
<table class="table">
<thead>
<tr>
<td><strong>Title</strong></td>
<td><strong>Job</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows">
<td><p v-show='row.showTitle' class="title">My title</p></td>
<td><input type="text" v-model="row.job"></td>
<td><a #click="row.showTitle = false">Remove title</a></td>
</tr>
</tbody>
</table>
Edit:
Here is jQuery version but as I already said this is a bad practice.
new Vue({
el: '.table',
data: {
rows: [
{ job: 'A' },
{ job: 'B' },
{ job: 'C' }
]
},
methods: {
hideTitle(index) {
$(this.$refs['title' + index]).hide();
}
}
});
<script src="https://unpkg.com/vue#2.5.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/jquery#3.2.1/dist/jquery.min.js"></script>
<table class="table">
<thead>
<tr>
<td><strong>Title</strong></td>
<td><strong>Job</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows">
<td><p class="title" :ref="'title' + index">My title</p></td>
<td><input type="text" v-model="row.job"></td>
<td><a #click="hideTitle(index)">Remove title</a></td>
</tr>
</tbody>
</table>
Try this one. it works. using v-f and title model
template
<table class="table">
<thead>
<tr>
<td><strong>Title</strong></td>
<td><strong>Job</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows">
<td v-if="title === false"><p class="title">My title</p></td>
<td><input type="text" v-model="row.job"></td>
<td><a #click="title()">Remove title</a></td>
</tr>
</tbody>
</table>
script
data () {
title:false,
},
methods: {
title(){
this.title= true
}
}
As others have mentioned, mixing jQuery and Vue is not the best idea. However, if you must, you can use the event.target that #flypan mentioned along with some jQuery selectors to get what you want.
I put together a JSFiddle using your HTML as an example of what can be done:
new Vue({
el: '#app',
data: {
rows: [
{ job: 'A' }, { job: 'B' }
]
},
methods: {
title: function() {
$title = $(event.target).parent().parent().find("p");
$($title).hide();
}
}
});
You would probably want to tighten the selector to find the "p", but this is just an example. Here's the working JSFiddle:
https://jsfiddle.net/psteele/p5Lpxj5a/
For conditionally displaying, you can use v-show directive,document link here :
v-show doucment.
Hope this can be helpful to you!