javascriptvue.jsvue-componentobject-propertiesvue-reactivity

How can I get Vue.js to be reactive for changes to dynamically added array properties?


In Vue.js, I have a data object with dynamically added/edited properties that are themselves arrays. For example, the property starts out as follows:

data: function () {
    return {
        vals: {}
    };
}

And over time, through various button clicks, etc., vals may look like the following (with the actual property names and values being 100% dynamic based on a number of factors):

vals: {
    set1: [
        {
            prop1: 123,
            prop2: 'hello'
        },
        {
            prop1: 456,
            prop2: 'bye'
        }
    ],
    set2: [
        {
            prop3: 'Why?!',
            prop4: false
        }
    ]
}

As the array properties (i.e., set1 and set2) are changed, I want to be able to react to those changes.

For example, I may do something like the following in my code:

var prop = 'set1';

this.vals[prop].push({
    {
        prop1: 789,
        prop2: 'hmmm...'
    }
});

However, when I do that, the component is not updating (I presume because I am pushing an object onto the end of a subarray of an object; and Vue.js doesn't seem to track those changes).

I have been able to force the component to be reactive by doing this.$forceUpdate(); after the above push, but I imagine there has to be a more eloquent way of getting Vue.js to be reactive when it comes to objects being pushed onto the end of object subarrays.

Does anyone know of a better way to try to do what I am trying to achieve? Thank you.


Solution

  • Any time you're adding a new property to an object or changing a value within an array, you need to use Vue.set().

    Vue.set(this.vals, prop, [ /* ... */ ])
    

    Ideally, you should define all your properties up front so Vue doesn't have to invalidate computed properties depending on your data model's shape. Even if you have them set to null you should try to map out all the properties you expect your component to need.

    Also, in your first code block, you have a colon after your return: which would evaluate to a label, meaning your data function isn't returning anything.