Categories
JavaScript Vue

Vue.js Transition Effects — State Transitions

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to create state transitions with GreenSock, tween.js, and Color.js

Animating State with Watchers

Watchers let us animate changes of any numerical property into another property.

For example, we can animate changes for a number that we input as follows with GreenSock and Vue:

src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    num: 0,  
    tweenedNumber: 0  
  },  
  computed: {  
    animatedNumber() {  
      return this.tweenedNumber.toFixed(0);  
    }  
  },  
  watch: {  
    num(newValue) {  
      TweenLite.to(this.$data, 0.5, { tweenedNumber: newValue });  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <input v-model.number="num" type="number" />  
      <p>{{ animatedNumber }}</p>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The code will above will animate the number changes as we enter a number into the input.

We watch the num value and then called:

TweenLite.to(this.$data, 0.5, { tweenedNumber: newValue });

to update the number display by incrementing or decrementing the number until it reaches the number we typed in.

TweenLite is from GreenSock.

We can also do the same thing for other things like a color string.

For example, we can write the following to animate the update of a color string:

src/index.js :

const Color = net.brehaut.Color;new Vue({  
  el: "#app",  
  data: {  
    colorQuery: "",  
    color: {  
      red: 0,  
      green: 0,  
      blue: 0,  
      alpha: 1  
    },  
    tweenedColor: {}  
  },  
  created() {  
    this.tweenedColor = Object.assign({}, this.color);  
  },  
  watch: {  
    color() {  
      const animate = () => {  
        if (TWEEN.update()) {  
          requestAnimationFrame(animate);  
        }  
      };
      new TWEEN.Tween(this.tweenedColor).to(this.color, 750).start();
      animate();  
    }  
  },  
  computed: {  
    tweenedCSSColor() {  
      return new Color({  
        red: this.tweenedColor.red,  
        green: this.tweenedColor.green,  
        blue: this.tweenedColor.blue,  
        alpha: this.tweenedColor.alpha  
      }).toCSS();  
    }  
  },  
  methods: {  
    updateColor() {  
      this.color = new Color(this.colorQuery).toRGB();  
      this.colorQuery = "";  
    }  
  }  
});

src/styles.css :

.color-preview {  
  width: 50px;  
  height: 50px;  
}

index.js :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <script src="https://cdn.jsdelivr.net/npm/tween.js@16.3.4"></script>  
    <script src="https://cdn.jsdelivr.net/npm/color-js@1.0.3"></script>  
    <link href="./src/styles.css" type="text/css" />  
  </head>  
  <body>  
    <div id="app">  
      <input  
        v-model="colorQuery"  
        @keyup.enter="updateColor"  
        placeholder="Enter Color"  
      />  
      <button @click="updateColor">Update</button>  
      <div  
        class="color-preview"  
        :style="{ backgroundColor: tweenedCSSColor }"  
      ></div>  
      <p>{{ tweenedCSSColor }}</p>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The code works above by getting the colorQuery string that was inputted, converting it to a Color object and assign it to this.color .

Once the this.color updates, The animate function in the color watcher in the watch property will be called.

Then this.tweenedColor will be updated with:

new TWEEN.Tween(this.tweenedColor).to(this.color, 750).start();

Then once this.tweenedColor is updated, then tweenedCSSColor is updated and displayed on the screen.

TWEEN.Tween is a constructor from tween.js.

Organizing Transitions into Components

We can put our transition code into its component. This way, we can reuse it and separate out the complexity of the transitions from other business logic.

For example, we can refactor our original example into the following code:

src/index.js :

Vue.component("num-transition", {  
  props: ["num"],  
  data() {  
    return {  
      tweenedNumber: 0  
    };  
  },  
  computed: {  
    animatedNumber() {  
      return this.tweenedNumber.toFixed(0);  
    }  
  },  
  watch: {  
    num(newValue) {  
      TweenLite.to(this.$data, 0.5, { tweenedNumber: newValue });  
    }  
  },  
  template: `<p>{{animatedNumber}}</p>`  
});new Vue({  
  el: "#app",  
  data: {  
    num: 0  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <input v-model.number="num" type="number" />  
      <num-transition :num="num"></num-transition>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we moved the transition logic into its own component by moving out all the transition logic and then passing the number that we want to watch for creating the transition as a prop.

Then we can change it slightly so that we can reuse it as follows:

src/index.js :

Vue.component("num-transition", {  
  props: ["num"],  
  data() {  
    return {  
      tweenedNumber: 0  
    };  
  },  
  computed: {  
    animatedNumber() {  
      return this.tweenedNumber.toFixed(0);  
    }  
  },  
  watch: {  
    num(newValue) {  
      TweenLite.to(this.$data, 0.5, { tweenedNumber: newValue });  
    }  
  },  
  template: `<span>{{animatedNumber}}</span>`  
});new Vue({  
  el: "#app",  
  data: {  
    num1: 0,  
    num2: 0  
  },  
  computed: {  
    result() {  
      return this.num1 + this.num2;  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src=https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <input v-model.number="num1" type="number" />  
      <input v-model.number="num2" type="number" />  
      <p>  
        <num-transition :num="num1"></num-transition> +  
        <num-transition :num="num2"></num-transition> =  
        <num-transition :num="result"></num-transition>  
      </p>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we changed the template of num-transition to:

<span>{{animatedNumber}}</span>

and then in index.html we changed the code to:

<div id="app">  
  <input v-model.number="num1" type="number" />  
  <input v-model.number="num2" type="number" />  
  <p>  
    <num-transition :num="num1"></num-transition> +  
    <num-transition :num="num2"></num-transition> =  
    <num-transition :num="result"></num-transition>  
  </p>  
</div>

Then we get:

https://thewebdev.info/wp-content/uploads/2020/04/calc.png

Conclusion

We can create transitions for component states by watching them and creating computed values for them.

Then we can use the GreenSock library to generate new values from the watched value.

Then that value can be displayed on the screen, creating an animation effect.

We can refactor the logic into its own component so that it won’t make a component too complex by mixing transition logic and other business logic.

Categories
JavaScript Vue

Introduction to Vue.js Templates

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to create and use templates to display data and handle events.

How it Works

Vue templates let us bind data to the DOM.

Vue compiles the template into Virtual DOM render functions. It can figure out the minimal number of components to re-render and minimize DOM manipulation when data changes.

Interpolations

The most basic form of data binding is text interpolation with double curly braces.

For example, we can write:

<p>{{foo}}</p>

To show the value of foo from a Vue instance or component.

The code above will be updated when foo updates.

We can keep it from updating after the first render by using the v-once directive as follows:

<p v-once>{{foo}}</p>

Raw HTML

We can also put raw HTML into an element with the v-html directive.

This lets us add HTML elements and formatting into an element.

For example, if we have the following in src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    rawHtml: "<b>bar</b>"  
  }  
});

and the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <p v-html="rawHtml"></p>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then we get bar bolded displayed since the inner HTML of the p element is <b>bar</b> .

When we’re using this, we have to sanitize the HTML so that it won’t contain any code. This is to prevent cross-site scripting attacks since code will be run if there are any when we pass in a value to the v-html directive.

Attributes

We can set values of HTML element attributes dynamically with v-bind as follows.

In src/index.js , we write:

new Vue({  
  el: "#app",  
  data: {  
    isButtonDisabled: true  
  }  
});

Then when we write the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button v-bind:disabled="isButtonDisabled">Button</button>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then since we set the isButtonDisabled property to true , the button will be disabled. This also apply to any other truthy value.

On the other hand, if we set it to false , we see that the button is disabled. This also applies to other falsy values like null , undefined , 0 and empty string.

JavaScript Expressions

We can add any other JavaScript expressions between the curly braces.

For example, we can write:

{{ number + 10 }}  
{{ yes ? 'YES' : 'NO' }}  
{{ str.split('').reverse('').join('') }}

Also, we can write expressions as the value of the v-bind directive as follows:

<div v-bind:id="`div-${id}`"></div>

Statements will not run inside the curly braces. For example:

{{ let a = 1 }}

will give us an error. We’ll get the error ‘avoid using JavaScript keyword as the property name: “let”’ in the console.

We also can’t run conditional statements within curly braces:

{{ if (id) { return id } }}

We’ll get the error ‘avoid using JavaScript keyword as the property name: “if”’.

Directives

Directives are special attributes with the v- prefix. We can pass in a single JavaScript expression as the value.

v-for can accept more than one expression separated by ; .

For example, we can conditionally display content with v-if :

<p v-if="show">Hello</p>

If show is truthy, then we’ll see Hello . Otherwise, it won’t be shown.

Arguments

Some directive takes an argument, which is denoted by a colon after the directive name.

For example, we can set the href value of an a element by writing:

<a v-bind:href="'https://medium.com'">Medium </a>

The v-on directive also takes an argument. For example, we can handle the click event of an element by writing:

<a v-on:click="onClick"> Click Me </a>

Then the onClick method in our Vue instance or component will be run when we click on the a element.

Dynamic Arguments

Since Vue.js 2.6.0, we can use a JavaScript expression as an argument for a directive.

For example, we can use it as follows. In src/index.js , we write:

new Vue({  
  el: "#app",  
  data: {  
    click: `${"cl"}${"ick"}`  
  },  
  methods: {  
    onClick() {  
      alert("clicked");  
    }  
  }  
});

Then in index.html , we can use a dynamic argument as follows:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <a v-on:click="onClick"> Click Me </a>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

This will reference the value of the click property of the data property in our Vue instance, and so we actually have v-on:click .

And therefore, when we click the a tag, we get an alert box with the clicked text displayed.

Dynamic argument expressions must evaluate to a string or null .

Spaces and quotes also aren’t valid inside the brackets.

Conclusion

We can create templates in various ways. The most basic is interpolation with the double curly brace syntax. We can put any JavaScript expression between the braces.

To set the values of attributes dynamically, we can use the v-bind directive. The value can be any JavaScript expression.

Some directives take an argument, which is the part of the directive name after the colon. Again the directive’s value can be any JavaScript expression in this case.

Dynamic arguments can be used since Vue 2.6.0. We can use it by putting an expression in square brackets.

Categories
JavaScript Vue

Introduction to Event Handling in Vue.js

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to handle various events with Vue.js and modify an event handler’s behavior.

Listening to Events

We can use the v-on directive to listen to DOM events and run some JavaScript code when they’re triggered.

For example, we can use it as follows:

src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    count: 0  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button v-on:click="count += 1">Add 1</button>  
      <p>Count: {{count}}</p>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when we click Add 1, the count increases by 1.

Method Event Handlers

We can also run methods when an event is triggered. This helps us a lot because we might need to runs lots of code when an event happens, so we can’t put it all in an expression in the template.

For example, we can set a method as the value of the v-on:click as follows:

src/index.js :

new Vue({  
  el: "#app",  
  methods: {  
    onClick() {  
      alert("clicked");  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button v-on:click="onClick">Click Me</button>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when Click Me is clicked, we get a ‘clicked’ alert box displayed.

Note that we just have to pass in the function reference, we don’t have to call it in the v-on:click directive.

Methods in Inline Handlers

In addition to passing in the method reference, we can also call methods directly with the v-on:click directive.

For example, we can write the following:

src/index.js :

new Vue({  
  el: "#app",  
  methods: {  
    onClick(message) {  
      alert(message);  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button v-on:click="onClick('hi')">Say Hi</button>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

We’ll get an alert box that says ‘hi’ when we click Say Hi.

Sometimes we have to access the original DOM element that triggered the event. We can use the $event object to do this.

The $event object has the triggered event’s data, which includes the DOM element that triggered the event.

For example, we can get the ID of the button that was clicked as follows:

src/index.js :

new Vue({  
  el: "#app",  
  methods: {  
    onClick(event, message) {  
      alert(`${event.target.id} says ${message}`);  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button id="hi-button" v-on:click="onClick($event, 'hi')">Say Hi</button>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The code above passes the $event object to the onClick handler, which takes the event parameter, which is set to the $event object and get the ID of the button via event.target.id . The message string is also passed in.

Then alert is called with the string `${event.target.id} says ${message}` so when we click Say Hi, we get an alert box with that says hi-button says hi since the buttons’ ID is hi-button .

Event Modifiers

We can use event modifiers to change the behavior of event handlers.

For example, the .prevent modifier for the v-on directive will automatically run event.preventDefault on the event handler function that’s set as the value.

The .prevent modifier can be used as follows:

<form v-on:submit.prevent="onSubmit"> ... </form>

Then event.preventDefault() will be run when the onSubmit handler is run before the rest of the code for onSubmit is run.

Other event modifiers include:

  • .stop
  • .capture
  • .self
  • .once
  • .passive

.stop runs event.stopPropagation() before the rest of the event handler code is run.

.capture let us capture the event. That is, when we run the event handler in an inner element, then the same event handler will also run in the outside elements.

For example, if we have the following in src/index.js:

new Vue({  
  el: "#app",  
  data: {},  
  methods: {  
    onClick() {  
      alert("clicked");  
    }  
  }  
});

and the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head><body>  
    <div id="app">  
      <div>  
        <a v-on:click.capture="onClick"> Click Me </a>  
      </div>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then when we click Click Me, we’ll see the ‘clicked’ alert box since the onClick handler is called on the parent div element.

.self will only trigger the event handler if the event.target is the element itself.

.once will trigger an event handler at most once.

For example, if we have the following in src/index.js:

new Vue({  
  el: "#app",  
  data: {},  
  methods: {  
    onClick() {  
      alert("clicked");  
    }  
  }  
});

and the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <div>  
        <a v-on:click.once="onClick"> Click Me </a>  
      </div>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then we only see the ‘clicked’ alert box once even though we click ‘Click me’ multiple times.

.passive will set the addEventListener ‘s passive option to true . passive set to true means that preventDefault() will never be called.

It’s used for improving performance on mobile devices.

Conclusion

We can set event handlers for DOM element events with thev-on directive.

To use it, we can pass in an expression, event handler method reference, or a call for an event handler as the value of it.

We can also get the data for the event that was triggered by the $event object, where we can get the target element of the event triggered.

Finally, we can add event modifiers to change the behavior of event handlers.

Categories
JavaScript Vue

Vue.js Transition Effects — List Transitions

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to add transitions when rendering lists.

List Transitions

If we want to apply transitions to render multiple items simultaneously with v-for , we can use the transition-group component.

Unlike transition , it renders a span by default. We can change the element that’s rendered with the tag attribute.

Transition modes aren’t available because we aren’t alternating between mutually exclusive elements.

Elements inside are always required to have a unique key attribute.

CSS transition classes will be applied to inner elements and not the group or container itself.

List Entering/Leaving Transitions

For example, we can make add a transition when adding or removing the item to a list as follows:

src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    items: [1, 2, 3],  
    nextNum: 4  
  },  
  methods: {  
    add() {  
      this.items.push(this.nextNum++);  
    },  
    remove() {  
      this.items.pop();  
    }  
  }  
});

src/style.css :

.list-enter-active,  
.list-leave-active {  
  transition: all 1s;  
}  
.list-enter,  
.list-leave-to {  
  opacity: 0;  
  transform: translateY(30px);  
}

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <link  
      rel="stylesheet"  
      type="text/css"  
      href="./src/styles.css"  
      media="screen"  
    />  
  </head>  
  <body>  
    <div id="app">  
      <button @click="add">Add</button>  
      <button @click="remove">Remove</button>  
      <transition-group name="list" tag="p">  
        <span v-for="item in items" :key="item">  
          {{ item }}  
        </span>  
      </transition-group>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when we click the Add button, we see a number added to the end of the list. When we click Remove, the rightmost number is removed.

Like transition , we defined the classes prefixed with the name we specified.

Also, we specified the tag name so that we have a span element rendered for each item within the p element.

List Move Transitions

transition-group can animate the change in position, in addition, to enter and leave. We can do this with the addition of the v-move class.

Like other classes, we prefix it with the name attribute and we can also manually specify a class with the move-class attribute.

For example, we can add an effect when we shuffle the position of numbers as follows:

src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    items: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  
  },  
  methods: {  
    shuffle() {  
      this.items = _.shuffle(this.items);  
    }  
  }  
});

src/styles.css :

.flip-list-move {  
  transition: transform 1s;  
}

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>  
    <link rel="stylesheet" type="text/css" href="./src/styles.css" />  
  </head>  
  <body>  
    <div id="app">  
      <button @click="shuffle">Shuffle</button>  
      <transition-group name="flip-list" tag="div">  
        <div v-for="item in items" :key="item">  
          {{ item }}  
        </div>  
      </transition-group>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

When we click Shuffle, we see the shuffling is stretched slightly in time.

Vue uses FLIP to smoothly transition between elements from the old position to their new position.

They don’t work with elements set to display: inline , we have to use display: inline-block or place elements in a flexbox container.

Staggering List Transitions

We can stagger list transitions as follows:

src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    items: ["foo", "bar", "baz"],  
    query: ""  
  },  
  computed: {  
    filteredItems() {  
      if (!this.query) {  
        return this.items;  
      }  
      return this.items.filter(i => i === this.query);  
    }  
  },  
  methods: {  
    beforeEnter(el) {  
      el.style.opacity = 0;  
      el.style.height = 0;  
    },  
    enter(el, done) {  
      var delay = el.dataset.index * 150;  
      setTimeout(() => {  
        Velocity(el, { opacity: 1, height: "1.6em" }, { complete: done });  
      }, delay);  
    },  
    leave(el, done) {  
      var delay = el.dataset.index * 150;  
      setTimeout(() => {  
        Velocity(el, { opacity: 0, height: 0 }, { complete: done });  
      }, delay);  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <input v-model="query" />  
      <transition-group  
        name="flip-list"  
        tag="div"  
        v-bind:css="false"  
        v-on:before-enter="beforeEnter"  
        v-on:enter="enter"  
        v-on:leave="leave"  
      >  
        <div v-for="item in filteredItems" :key="item">  
          {{ item }}  
        </div>  
      </transition-group>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

We can see that we now have transitions when the list is being filtered.

Conclusion

We can create transitions when rendering a list with v-for like we do with v-if .

The difference is that we have to specify the tag name of the container if we don’t want to render a span.

Also, we always have to add the key attribute to each element inside.

We can have transitions when changing element position by specifying the CSS style for the v-move class, which can be renamed if we specify the class prefix or name.

Specifying enter and leave transitions or v-for is the same as specifying enter and leave transitions for v-if .

Finally, we can create transitions with JavaScript with Velocity.js just like v-if animations.

Categories
JavaScript Vue

Introduction to Vue.js Computed Properties and Watchers

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at Vue.js computed properties and watchers.

Computed Properties

To make more template expressions more concise and reusable, we can create computed properties to compute results from existing properties as their value changes.

We can define computed properties in a Vue instance by putting them in the computed property of the options object that we pass into the Vue constructor.

For example, we can write the following in src/index.js:

new Vue({  
  el: "#app",  
  data: {  
    message: "foo"  
  },  
  computed: {  
    spelledMessage() {  
      return this.message.split("").join("-");  
    }  
  }  
});

Then we can use it like any other field in our HTML template. So in index.html, we can write:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <p>{{message}}</p>  
      <p>{{spelledMessage}}</p>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

We should then get:

foof-o-o

on our screen.

The spelledMessage method is a getter that’s used by Vue.js. It takes the returned value of it and then put it in place of {{spelledMessage}} .

Therefore, the name of the placeholder in the template must be the same as the name of the method that returns the value that we want in the computed object.

We can also get the value from the returned Vue instance as follows:

const vm = new Vue({  
  el: "#app",  
  data: {  
    message: "foo"  
  },  
  computed: {  
    spelledMessage() {  
      return this.message.split("").join("-");  
    }  
  }  
});console.log(vm.spelledMessage);

We should see f-o-o logged from the console.log.

We can bind to computed properties in templates like any other property. Vue knows that vm.spelledMessage depends on vm.message, so the bindings will be updated when vm.message is updated.

The computed getter method doesn’t commit side effects, which makes it easy to test and understand.

Computed Caching vs Methods

One good feature of computed properties is that the result of it is cached.

There’s no caching for results returned from methods.

So instead of using methods, we should use computed properties for cases where we’re computing something from existing properties.

A computed property is only re-evaluated once a reactive property is updated.

For example, if we have:

const vm = new Vue({  
  el: "#app",  
  data: {  
    message: "foo"  
  },  
  computed: {  
    now() {  
      return Date.now();  
    }  
  }  
});

It’ll never update after its first computed since it doesn’t depend on any reactive property from the data property.

Without caching computed properties keep getting computed as reactive properties that they depend on change, which means the app gets slow as reactive properties that the computed property depends on are changing.

Computed vs Watched Property

Watch properties are useful for watching for changes in a property as the name suggests.

However, because of the caching feature of computed properties, we should use them as much as we can.

For instance, we can simplify the following:

new Vue({  
  el: "#app",  
  data: {  
    name: "Joe",  
    age: 1,  
    person: undefined  
  },  
  watch: {  
    name: {  
      immediate: true,  
      handler(newVal) {  
        this.person = `${newVal} - ${this.age}`;  
      }  
    },  
    age: {  
      immediate: true,  
      handler(newVal) {  
        this.person = `${this.name} - ${newVal}`;  
      }  
    }  
  }  
});

to:

new Vue({  
  el: "#app",  
  data: {  
    name: "Joe",  
    age: 1  
  },  
  computed: {  
    person() {  
      return `${this.name} - ${this.age}`;  
    }  
  }  
});

As we can see, the first example was much more complex. We have to set immediate to true in each property so that it’ll watch the initial value change. Then we have to define the value change handler for each reactive property.

Then in each handler, we have to set the person property so that we can combine name and age.

On the other hand, with computed properties, we only have one method which returns the fields combined in the way we want.

It’s much more convenient for us and the app that we create is also faster because of caching.

Computed Setter

We can also create a setter function for computed properties.

For example, we can write:

new Vue({  
  el: "#app",  
  data: {  
    name: "Joe",  
    age: 1  
  },  
  computed: {  
    person: {  
      get() {  
        return `${this.name} - ${this.age}`;  
      },  
      set(newValue) {  
        const [name, age] = newValue.split("-");  
        this.name = name.trim();  
        this.age = age.trim();  
      }  
    }  
  }  
});

The getter function returns the same thing as before, but now we also have a setter function.

The setter function splits the newValue, which has the computed value from the getter function, we split the result and set the result back to the corresponding reactive properties.

We can use a setter as follows. We can put the following in src/index.js:

new Vue({  
  el: "#app",  
  data: {  
    firstName: "Joe",  
    lastName: "Smith"  
  },  
  computed: {  
    name: {  
      get() {  
        return `${this.firstName} ${this.lastName}`;  
      },  
      set(newValue) {  
        const [firstName, lastName] = newValue.split(" ");  
        this.firstName = (firstName || "").trim();  
        this.lastName = (lastName || "").trim();  
      }  
    }  
  }  
});

And in index.html, we put:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <p>{{name}}</p>  
      <input type="text" v-model="name" />  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then when we change the value in the input, we get the new values assigned back to the firstName and lastName fields with the setter.

Conclusion

We can use computed properties to compute values from existing properties.

Returned values from computed properties are cached to reduce the amount of computation required.

Computed properties can also have setters, where we can do things with the new values returned from the computed property getter.