Изменение сложного вычисляемого объекта в vue.js


У меня есть сложный объект для таблицы. Выглядит так:

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

Это код из шаблона, который использует этот объект:

    <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>

Я оценил его как вычисляемое свойство, но мне нужно повторно отобразить таблицу, когда родительский компонент предоставляет данные асинхронно, поэтому у меня есть наблюдатель за реквизитом (реквизит называется «действия»):

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
    }
},

Проблема в том, что таблица не перерисовывается. Точнее, компонент "hour-td" не содержит новых данных.
Также я пытался использовать Vue.set, но безуспешно

Не могли бы вы помочь мне с обновлением таблицы? ? Я потратил около 5 часов на рефакторинг и попытки.
Заранее спасибо

РЕШЕНИЕ
В моем случае может быть два состояния: есть действия, нет действий. Поэтому я сделал два вычисляемых реквизита для каждого случая, отрисовывал их отдельно и переключался на v-if="activities.length"


person Вася Аристов    schedule 13.11.2017    source источник
comment
Трудно получить общую картину только из кода, который вы опубликовали, но я раньше работал с датами и вычисляемыми свойствами, и Vue не увидит Date изменение как обновление. Вычисляемые свойства кэшируются и будут обновляться только при обновлении их зависимостей, поэтому, вероятно, ваш компонент hour-td не работает. Первое, что я бы попробовал, это создать new Date() из метки времени, которая есть в вашем вычисляемом свойстве.   -  person Stephan-v    schedule 13.11.2017
comment
Да, вы правы насчет кеша. Но проблема не в датах (они всегда в отметке времени, поэтому я работаю как с числами, они тоже приходят с сервера) и не в кеше (использую вотчер, чтобы перезаписывать кеш). Здесь вы можете найти полный код компонента github.com/Vasek18/my-hours.ru/blob/master/resources/assets/js/   -  person Вася Аристов    schedule 13.11.2017


Ответы (1)


Я думаю, что ваша проблема связана с известной проблемой Vue для Change Detection Caveats (вы можете прочитать здесь) с прямым назначением массива, которые не обнаруживают изменений.

Вы должны изменить эту часть кода (с прямым назначением массива):

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;
   }
}

С опцией Vue.set() для массивов, чтобы обнаружить изменение и повторно визуализировать компонент. Это работало для меня в разных случаях:

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

Подробнее здесь: https://codingexplained.com/coding/front-end/vue-js/array-change-detection

Редактировать:

Теперь я вижу, что ты сказал:

Также я пытался использовать Vue.set, но безуспешно.

Что вы имеете в виду под: «без успеха»? Можете ли вы поделиться кодом? У меня такая же проблема, и я решил ее с помощью Vue.set..

Вы также можете взглянуть на vm.$forceUpdate(), попытаться выполнить после последнего console.log или взять весь код внутри vm.$nextTick( [callback] ), чтобы выполнить все действия (загрузить данные в таблицу), а затем повторно отрендерить компонент на следующем галочка.

Подробнее здесь: https://vuejs.org/v2/api/#vm-forceUpdate && https://vuejs.org/v2/api/#Vue-nextTick< /а>

Редактировать 2:

Я думаю, что ваша проблема связана с индексом массива, вы должны посмотреть здесь: https://vuejs.org/v2/guide/list.html#Array-Change-Detection . Попробуйте изменить:

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;
                    }
                }

и упростить с помощью:

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);
}

Надеюсь, поможет!

person JP. Aulet    schedule 13.11.2017
comment
Спасибо за ответ, но я все еще не могу решить эту проблему. Итак, я попробовал Vue.set(vm.tds[dateTimestamp].hours[activity.start_at].activity, 'activity', activity.activity); и Vue.set(vm.tds, dateTimestamp, vm.tds[dateTimestamp]); и пусть temp = activity.start_at; Vue.set(vm.tds[dateTimestamp].hours, activity.start_at, {ключ: {активность: активность}}); в цикле. Безрезультатно. Также попробуйте это.$children.forEach(function(child){ child.$forceUpdate(); });. Тоже без успеха - person Вася Аристов; 13.11.2017
comment
Спасибо за вашу помощь. Такой подход не работает, потому что это не массив, а объект, и потому, насколько я понимаю, vue не может сделать реактивным объект в объекте в объекте. Так что у меня есть другое решение. Два отдельных реквизита для случаев существования активностей. Посмотреть можно здесь github.com/Vasek18/my-hours.ru/blob/master/resources/assets/js/ - person Вася Аристов; 14.11.2017
comment
В порядке! Я рад, что вы нашли решение. Попробуйте отредактировать и ответить на свой вопрос, чтобы в будущем помочь кому-то с такой же проблемой! Если ответ помог, проголосуйте ;) - person JP. Aulet; 14.11.2017