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.
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:
- Changing elements at a particular index.
- 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.