How to dynamically create reactive properties in Vue

How to dynamically create reactive properties in Vue


One of the best features that JavaScript frameworks are known for is their reactivity and Vue is no exception. In this tutorial, we’ll be looking at how to add reactive properties in a component’s instance after initialization.

Before we kick off

Learn Vue.js and modern, cutting-edge front-end technologies from core-team members and industry experts with our premium tutorials and video courses on VueSchool.io.

Click here to Browse all Courses on VueSchool.io

How changes are tracked in Vue

When an object is passed as the data option to a Vue instance, Vue walks through every property in that object swapping them with getters/setters using Object.defineProperty.

Vue does not allow adding new root-level properties to the data object after the initialization of a component. This is because Vue modifies every property in the data object on initialization so when the properties change, Vue is notified of the changes.

const vm = new Vue({
  data: {
    a: 'hello' // this will be reactive
  }
});

vm.b = 'world'; // not reactive, and won't appear in the template either

If there’s a template element referencing b, there will be an error in the console:

Property or method "b" is not defined on the instance but referenced during render Make sure to declare reactive data properties in the data option.

This is Vue letting you know that b was never added to the component instance.

A clever workaround would be to initialize b but set its value to undefined since the value will be assigned based on some operation later on.

const vm = new Vue({
  data: {
    a: 'hello',
    b: undefined
  }
});

vm.b = 'world'; // totally works

How to create new reactive properties

Since Vue does not allow creating new root-level elements, there are ways to create your own reactive properties after Vue has been initialized which we’ll be looking at now

Using Vue.set()

In order for this to work, the reactive property being created has to be set on an existing property on the data option in Vue. This should be an object, so what you’ll be doing, in essence, is adding a new property on a nested object. Do not try to add the property directly, that still won’t work.

import Vue from 'vue';

const vm = new Vue({
  data: {
    user: {
      first_name: 'John'
    }
  }
});

Vue.set(vm.user, 'last_name', 'Doe'); // reactive

Using Vue.set(), you’re letting Vue know that you’re creating another property and it’ll track that property now. The syntax for doing that is: Vue.set(object_name, new_property, new_value). To use the .set() method inside a component instance, use .$set(), it is an alias for Vue.set(), so the syntax will look like this:

this.$set(this.user, 'last_name', 'Doe')

Using Object.assign()

You can use Object.assign() if you’ll be adding more than one property on the existing object.

const vm = new Vue({
  data: {
    user: {
      first_name: 'Jane'
    }
  }
})
vm.user = Object.assign({}, vm.user, { first_name: 'Jane', last_name: 'Doe', age: 45});

The reason we created a new object with new properties and old ones is so Vue will run through all properties now and make them reactive. If we only added new properties to the object, Vue would not be aware.

It’s also possible to do that in a component instance too, by doing it this way:

this.user = Object.assign({}, this.user, { first_name: 'Jane', last_name: 'Doe', age: 45});

The techniques listed above are the solutions to the known caveat for reactivity in objects, there are also known caveats when dealing with arrays which we’ll be looking at next.

Adding reactive elements to arrays

For the changes made in an array at a particular index appear in the DOM, we cannot use the typical way of adding elements to the array which is:

arr[index] = 25; // this will not trigger any changes in the DOM

There are two known issues when dealing with reactivity in arrays:

  1. Changing elements at a particular index.
  2. Changing the length of the array.

Solutions

To fix caveat one, we can use the Array.prototype.slice method and Vue.set() also.

Using Array.prototype.slice:

const vm = new Vue ({
  data: {
    fruits: ['orange', 'pineapple']
  }
});

vm.fruits.splice(0, 1, 'banana'); // this replaces orange with banana and updates the dom.

Using Vue.set():

Vue.set(vm.fruits, 0, 'kiwi'); // remplaces the first element in the array with kiwi

The syntax is: Vue.set(array, index, newValue)

Also, remember that we can use the .$set() alias directly inside the component.

To read more on reactivity in Vue you can checkout the reactivity in-depth doc.


Share on social media

//