13

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.

3 Answers 3

20

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>

Sign up to request clarification or add additional context in comments.

Comments

4

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/

11 Comments

thanks but not working i forgot to mention i'm already tried examples that can be found with basic google search before i'm asking here (and in my example as @Terry mention i'm working on single element see the key used) and to be more accurate you suggestion work only at close/hide phase not in show
I updated the answer with a working fiddle. I think there was an error with using the item for the key instead of item.data since Vue prefers you to key items with a string or number rather than an object.
interesting can you provide a link about this fact, for now there is no difference with object or string still no working at show item phase.
I got a warning in the console: [Vue warn]: <transition-group> children must be keyed: <tr>
I think the code is a little confused. What you are probably looking for is closer to this.
|
0

transition only works on single rendered element

if we move transition inside the element, it works

should also look into use of transition-group which may help

5 Comments

Thanks but I'm already tried with no success can you provide an example please
I think @mikl is using <transition> for singular elements, so your answer is incorrect.
e.g. "<tr v-bind:key="item" v-if="item.more"> <td> <transition name="fade" > <p >{{k + 1}} - {{item.data}}</p> </transition> </td> </tr> ..."
i see you've moved the transition inside the tr element this is not exactly what i'm trying to do
also got it working similar as other comment: <tr is="transition-group" name="fade"> <td v-bind:key="item" v-if="item.more" ><p >{{k + 1}} - {{item.data}}</p></td> </tr>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.