May 7, 2018

Simplify Your Components with Computed Setters

One of the main core concepts of Vue.js is computed properties. With computed properties we can easily compose new data that is derived from other data. And that new data will be cached until one of its dependencies changes.

Here’s a quick example on that:

data () {
  return {
    firstName: 'Foo',
    lastName: 'Bar'
  }
},

computed: {
  fullName () {
    return `${this.firstName} ${this.lastName}`
  }
}

fullName is what we call a computed getter — we use it to get the computed value. But what if we also want to set it?

We can define a computed setter for that, like this:

computed: {
  fullName: {
    get () {
      return `${this.firstName} ${this.lastName}`
    },

    set (fullName) {
      this.firstName = fullName.split(' ')[0]
      this.lastName = fullName.split(' ')[1]
    }
  }
}

Now, whenever you set a new value to fullname (using this.fullName = 'Example Name'), firstName and lastName will update accordingly.

Show me a practical example

When your component is using data from outside — like data passed via props or from vuex — you can simplify the way you interact with this data using computed setters.

Let’s take an example that at first doesn’t use computed setters, and then improve it by introducing computed setters to it.

Without computed setters:

<template>
  <div>
    <input :value="text" type="text" @input="onInput" />
    <button @click="convertToUpperCase">ToUpperCase</button>
  </div>
</template>

<script>
  export default {
    props: ['text'],
    methods: {
      onInput(e) {
        this.$emit('set-text', e.target.value)
      },
      convertToUpperCase() {
        this.$emit('set-text', this.text.toUpperCase())
      }
    }
  }
</script>

With computed setters:

<template>
  <div>
    <input v-model="textValue" type="text" />
    <button @click="convertToUpperCase">ToUpperCase</button>
  </div>
</template>

<script>
  export default {
    props: ['text'],
    computed: {
      textValue: {
        get() {
          return this.text
        },

        set(value) {
          this.$emit('set-text', value)
        }
      }
    },

    methods: {
      convertToUpperCase() {
        this.textValue = this.textValue.toUpperCase()
      }
    }
  }
</script>

Using computed setters improved our example in two ways:

  1. First, updating the external data feels much simpler. We now can replace :value and @input with v-model. Also we can change the value with a simple assignment — like what we have in convertToUpperCase method.
  2. Second, changing the source of the external data only requires updating the computed setter as opposed to replacing each $emit('set-text') in the component. So if you decided to change the source to using data from vuex, you would just need to change the setter method to something like this.$store.dispatch('SET_TEXT, value).
Stay up-to-date on the latest projects and articles from me