Changing complex computed object in vue.js


I have complicated object for a table. Looks like this:

{
    1510002000: {
        date: "07.11.17"
        hours: {
            1510002000:{
                activity:{
                    activity: "Тест",
                    color: "#00ff00",
                    end_at: 1510005600,
                    start_at: 1510002000,
                    type_id: 1
                }
            },
            1510005600: {
                ...
            },
            ...
        }
    },
    ....
}

This is a code from template that uses this object:

    <tr v-for="date in this.tds">
        <td>{{ date.date }}</td>
        <td is="hour-td"
            v-for="hour in date.hours"
            :color="hour.activity.color"
            :start_at="hour.activity.start_at"
            :end_at="hour.activity.end_at"
            :activity="hour.activity.activity"
            :type_id="hour.activity.type_id"
        >
        </td>
    </tr>

I evaluated it as a computed property, but I need to rerender table when parent component provides data assync, so I have a watcher for prop (prop called "activities"):

watch: {
    activities: function(){
        var vm  = this;
        let dth = new DateTimeHelper;

        if (this.activities.length > 0){
            this.activities.forEach(function(activity){
                let dateTimestamp = dth.getDateTimestampFromTimestamp(activity.start_at); // just getting the key

                if (vm.tds[dateTimestamp]){
                    if (vm.tds[dateTimestamp].hours[activity.start_at]){
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.color    = activity.color;
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id  = activity.type_id;
                    }
                }
            });
        }

        console.log(vm.tds) // here I can see that the object is filled with new data
    }
},

The problem is that the table doesn't rerender. More precisely, the component "hour-td" does not contain the new data.
Also I've tried to use Vue.set, but no success with that

Can you help me with the updating table? I've spent like 5 hours for refactoring and attempts.
Thanks in advance

SOLUTION
In my case there can be two states: there are activities, there are no activities. So I made two computed props for each case and render them separately and switch by v-if="activities.length"

Upvotes: 0

Views: 2327

Answers (1)

JP. Aulet
JP. Aulet

Reputation: 4408

I think that your problem is with Vue known issue for Change Detection Caveats (you can read here) with array direct assignation, that don't detect changes.

You should change this part of code (with direct array assignation):

if (vm.tds[dateTimestamp]){
   if (vm.tds[dateTimestamp].hours[activity.start_at]){
      vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
      vm.tds[dateTimestamp].hours[activity.start_at].activity.color    = activity.color;
      vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id  = activity.type_id;
   }
}

With the Vue.set() option for arrays in order to detect the change and re-renders the component. It worked for me in differents occassions:

// Vue.set(object, key, value)
// something like this: 
Vue.set(vm.tds[dateTimestamp].hours[activity.start_at].activity.activity,1,activity.activity);

More info here: https://codingexplained.com/coding/front-end/vue-js/array-change-detection

Edit:

I see now that you said:

Also I've tried to use Vue.set, but no success with that

What you mean with: "no success" ? Can you share the code? I have the same issue and I resolved with Vue.set..

You can also take a look to vm.$forceUpdate(), try to execute after the last console.log or grab all the code inside a vm.$nextTick( [callback] ) in order to execute all the actions (load data in the table) and then, re-render the component on next tick.

More info here: https://v2.vuejs.org/v2/api/#vm-forceUpdate && https://v2.vuejs.org/v2/api/#Vue-nextTick

Edit 2:

I think that your problem is with the index of the array, you should take a look here: https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection . Try changing the:

if (vm.tds[dateTimestamp]){
                    if (vm.tds[dateTimestamp].hours[activity.start_at]){
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.color    = activity.color;
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id  = activity.type_id;
                    }
                }
 

and simplify with:

if (vm.tds[dateTimestamp] && vm.tds[dateTimestamp].hours[activity.start_at]){
   Vue.set( vm.tds, vm.tds.indexOf(vm.tds[dateTimestamp].hours[activity.start_at]), activity);
}

Hope it helps!

Upvotes: 1

Related Questions