w3resource

Computed Properties and watchers

In-template expressions are convenient only for simple operations. When we put too much logic in our templates, they become bloated and thus hard to maintain:

<span id=?app?>
	{{information.split(??).reverse().join(??)}}
</span>

In the example above, the template is no longer declarative and simple. One has to take a second look to realize that it displays the information in reverse. This becomes worse when we need to include the reversed message in our template more than once.

Thus for any complex logic, use computed property.

Basic Example

HTML

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

JS

var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // a computed getter
    reversedMessage: function () {
      // `this` points to the vm instance
      return this.message.split('').reverse().join('')
    }
  }
})

Much as you would expect, the snippet above respectively displays:

Hello 
olleH

the value of reversedMessage is always dependent on the message property, hence vm.reversedMessage always changes with changes to vm.message.

Computed Caching vs Method

HTML:

<p>Reversed message: "{{ reverseMessage() }}"</p>

JS:

// in component
methods: {
  reverseMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

As shown in the snippet above, it is possible to a method to achieve the same result as a computed property. Both have the same result as seen. But computed properties are cached based on their reactive dependencies. A computed property will re-evaluate only when some of its reactive dependencies change, thus multiple access to the reversedMessage will return the previously computed result without running the function again. In contrast a method invocation will always run whenever a re-render occurs.

Caching is necessary whenever we don?t want to executes a property?s getter more than necessary, else use method instead.

Computed vs Watched Property

Vue provides a watch property which is a more generic way to observe and react to data changes on the Vue instance. It is however a good idea to always use computed property as against watch when you have some data that needs to change based on some other data. An example is as shown:

HTML

<div id="demo">{{ fullName }}</div>

JS

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

Below is a computed property version of the code above:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

Computed Setter

The computed properties are by default getter-only, however you can provide a setter when needed:

// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// 

Running vm.fullName = ?Aryan Yuksek?, the setter will be invoked and vm.lastName vm.lastName updated accordingly.

Watchers

Though computed properties are more appropriate in most cases, custom watcher is however necessary in certain times. This is the reason Vue provides us with th e watch option. The watch option especially useful when we want to perform async operations. For example:

<div id="watch-example">
  <p>Ask a yes/no question: <input v-model="question"></p>
  <p>{{ answer }}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script>
var watchExampleVM =new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'You have to ask a question, for me to give you an answer'
  },
  watch: {
    question: function (newQuestion,oldQuestion) {
      this.answer ='Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  },
  created: function () {
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
  },
  methods:{
    getAnswer: function () {
      if (this.question.indexOf('?') === -1) {
        this.answer = 'Questions usually contain a question mark. ;-)'
        return
      }
      this.answer = 'Thinking...'
      var vm = this
      axios.get('https://yesno.wtf/api')
        .then(function (response) {
          vm.answer = _.capitalize(response.data.answer)
        })
        .catch(function (error) {
          vm.answer = 'Error! Could not reach the API.'+ error
        })
    }
  }
})
</script>

As can be seen, the watch option allows us to perform an async operation, limit how often we perform the operation, and set intermediary states until we get final answer.

Previous: Template Syntax
Next: Class and Style Bindings



Follow us on Facebook and Twitter for latest update.