On the face of it creating a flash message box which fades in then fades out seems like it should be a straight forward task, however, because Vue is data driven we need to leverage transitions in order to have our message react the way we want it to.

In vue everything get’s bound to some underlying data property on the view model, we don’t touch the DOM, instead we update the bound property and the view is updated automatically. So to show and hide elements we can bind a boolean flag to the element, using v-show:

<div v-show="showMessage">{{message}}</div>

Now, we simply set the showMessage flag to true when we want to show the message and false when we want to hide it. However, it’s not exactly aesthetically pleasing to have things abruptly jump on and off the page. What we really want is a smooth animation and for this we use transitions.

Creating the Transition

For our flash message we want to create a quick fade-in effect, followed by a longer fade-out effect, so let’s look at how to do that.

Vue implements transitions by adding and removing specific classes to the transitioned element at certain points. For displaying our message we want to use the v-enter-active class, which get’s added before the element is inserted (essentially when we set showMessage to true), so this will be where we want to add the quick fade-in transition:

.fade-enter-active {
  transition: opacity 1s ease;
}

Note that we have called it .fade-enter-active not v-enter-active and that’s because Vue replaces the “v” with the name of the transition, which we will call “fade”.

For the fade-out, we – unsurprisingly – want to use the v-leave-active class, once again “v” gets replaced with the transition name, so we call it fade-leave-active:

.fade-leave-active {
  transition: opacity 3s ease;
}

Finally, we need to set the set our starting opacity for v-enter to apply the fade-in transition and we use v-leave-to (or if you are using Vue < 2.1.8 use v-leave-active instead ) to set the target opacity for our leave transition. Obviously, to fade in we want to start with an opacity of 0 and when we fade out we want to fade to 0. Once again "v" gets replaced by the transition name:

.fade-enter, .fade-leave-to{
  opacity: 0;
}

Now we can wrap our message in the transition, we just need to add the name "fade" to target the fade-xxx css attributes we just wrote. I've also attached the showMessage flag to v-show which will apply the relevant transition when it changes:

<transition name="fade">
  <message v-show="showMessage">Success</message>
</transition>

Message is actually a component I've created so it can be re-used. It's essentially a bootstrap alert box with a slot so we can pass in our message, and a prop, so we can pass in relevant bootstrap class ('success', 'danger') etc to style the alert:

Template

<template>
  <div :class="['alert', getAlertClass]">
    <slot></slot>
  </div>
</template>

Vue Instance

props:{
  type: {
    default: 'success',
    type: String
  }
},
computed: {
  getAlertClass(){
    return 'alert-'+this.type;
  }
}

Now our transition will fade-in when we set our showMessage to true and fade-out when we set showMessage to false. For the effect we are aiming for we want to fade-in then set our setMessage flag to false so we can start the fade-out effect. To do this we can use the v-on:after-enter hook – which will fire when the v-enter transition completes – to set showMessage to false:

Transition Markup

    <transition name="fade" v-on:after-enter="showMessage = false">
      <message v-show="showMessage">Success!</message>
    </transition>

Here's the JSFiddle to show the complete effect: https://jsfiddle.net/vuetiful/h525xt0q/

Reusing the Transition

Transitions can be re-used just like any other component, so let's take a look at how we might do that, here's the component:

Template

<template>
  <transition name="fade" v-on:after-enter="$emit('loaded');">
    <slot></slot>
  </transition>
</template>

Here the transition is completely unaware of any state; that still remains the job of the parent, so, in order to maintain our flash effect, we are emitting a loaded event on v-on:after-enter, which we can listen for inside our parent and then set the hide flag to false.

In order to make this work on any element, we have included a slot which will render anything we place between the custom component tags. So going back to our message example, we can now do the following:

<fade-transition @loaded="showMessage = false">
  <message v-show="showMessage">Success</message>
</fade-transition>

And here's the final result, a totally re-usable transition:
https://jsfiddle.net/vuetiful/kh3vL751/

Advertisements