I'm trying to have a transition (animation) on html table row (vue.js) with no success. Here's the full example
new Vue({
el: '#data',
data: {
items: [
{
data: 'd1',
more: false
},
{
data: 'd2',
more: false
},
]
}
});
.fade-enter-active, .fade-leave-active {
transition: opacity 2s
}
.fade-enter, .fade-leave-to {
opacity: 0
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div class="container-fluid" id="data">
<br>
<br>
<table border="1" class="table table-bordered">
<thead class="thead-inverse">
<tr>
<th>anim</th>
</tr>
</thead>
<tbody>
<template v-for="item, k in items">
<tr>
<td><button @click="item.more = !item.more" type="button"
v-bind:class="[item.more ? 'btn-danger' : 'btn-primary']" class="btn">Show the hidden row</button></td>
</tr>
<transition name="fade" >
<tr v-bind:key="item" v-if="item.more">
<td><p >{{k + 1}} - {{item.data}}</p></td>
</tr>
</transition>
</template>
</tbody>
</table>
</div>
I am expecting that the hidden table row should appear with transition/animation on opacity property when it appears, but nothing is happening, how am I supposed to do so? This is perfectly working on another element like span or other.
So, first off I want to point out that had you used a string template, then the code in your question would work as is.
console.clear()
new Vue({
el: '#data',
template: `
<div>
<br>
<br>
<table border="1" class="table table-bordered">
<thead class="thead-inverse">
<tr>
<th>anim</th>
</tr>
</thead>
<tbody>
<template v-for="item, k in items">
<tr>
<td><button @click="item.more = !item.more" type="button"
v-bind:class="[item.more ? 'btn-danger' : 'btn-primary']" class="btn">Show the hidden row</button></td>
</tr>
<transition name="fade" >
<tr v-bind:key="item" v-if="item.more">
<td><p >{{k + 1}} - {{item.data}}</p></td>
</tr>
</transition>
</template>
</tbody>
</table>
</div>
`,
data: {
items: [
{
data: 'd1',
more: false
},
{
data: 'd2',
more: false
},
]
}
});
.fade-enter-active, .fade-leave-active {
transition: opacity 2s
}
.fade-enter, .fade-leave-to {
opacity: 0
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div class="container-fluid" id="data">
</div>
Notice, the only change made in this example is that I turned the template for the Vue into a string instead of defining the template in the DOM. The reason this single change works is because when you use an "in DOM" template, you are subject to the browser parsing the HTML prior to Vue converting the template into a render function. When you are working with a table
element, browsers are very picky about what kinds of elements they will allow to be rendered inside the table; generally, only elements related to tables (thead
, tbody
, tr
, td
, etc.). transition
is not an element it gets along with apparently (though, somewhat surprisingly, it doesn't choke on template
).
String templates, however, are never parsed by the browser, they are directly translated into render functions and so they will work as you wrote them. So my first recommendation would be, just use a string template.
If, however you wanted to continue to use an in DOM template, we need to make a few changes to the code. First, we need to move the transition to a place where the browser will be happy. With a table we can easily do that by moving it to the tbody
tag and using Vue's special is
directive. Second, because our transition will now apply to multiple elements, we need to switch it to a transition-group
.
Because we're using a transition-group
each of the elements inside the transition must have a key. So, for each row, we just add a key for that row.
console.clear()
new Vue({
el: '#data',
data: {
items: [{
data: 'd1',
more: false
},
{
data: 'd2',
more: false
},
]
}
});
.fade-enter-active,
.fade-leave-active {
transition: opacity 2s
}
.fade-enter,
.fade-leave-to {
opacity: 0
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div class="container-fluid" id="data">
<br>
<br>
<table border="1" class="table table-bordered">
<thead class="thead-inverse">
<tr>
<th>anim</th>
</tr>
</thead>
<tbody name="fade" is="transition-group">
<template v-for="item, k in items">
<tr v-bind:key="`button-${item.data}`">
<td>
<button @click="item.more = !item.more"
type="button"
v-bind:class="[item.more ? 'btn-danger' : 'btn-primary']"
class="btn">Show the hidden row
</button>
</td>
</tr>
<tr v-bind:key="`detail-${item.data}`" v-if="item.more">
<td><p >{{k + 1}} - {{item.data}}</p></td>
</tr>
</template>
</tbody>
</table>
</div>
Try removing the <transition> tag altogether and doing this instead:
<tr name="fade" is="transition" v-bind:key="item.data" v-if="item.more">
Source: https://github.com/vuejs/vue/issues/3907#issuecomment-253111682
Fiddle: https://jsfiddle.net/c8vqajb4/3/
UPDATE:
We ended up using a transition-group:
<tbody name="fade" is="transition-group">
<tr class="row" v-bind:key="item.data" v-if="item.more">
<td><p >{{k + 1}} - {{item.data}}</p></td>
</tr>
</tbody>
As suggested here: https://v2.vuejs.org/v2/guide/transitions.html#List-Transitions
Fiddle: https://jsfiddle.net/c8vqajb4/4/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With