Как я могу заставить Vue.js реагировать на изменения динамически добавляемых свойств массива?

В Vue.js у меня есть объект данных с динамически добавляемыми / редактируемыми свойствами, которые сами по себе являются массивами. Например, свойство начинается следующим образом:

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

И со временем, при нажатии различных кнопок и т. Д., vals может выглядеть следующим образом (при этом фактические имена и значения свойств являются 100% -ными динамическими в зависимости от ряда факторов):

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

При изменении свойств массива (т.е. set1 и set2) я хочу иметь возможность реагировать на эти изменения.

Например, я могу сделать в своем коде что-то вроде следующего:

var prop = 'set1';

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

Однако, когда я это делаю, компонент не обновляется (я предполагаю, потому что я помещаю объект в конец подмассива объекта; и Vue.js, похоже, не отслеживает эти изменения).

Мне удалось заставить компонент быть реактивным, выполнив this.$forceUpdate(); после вышеуказанного push, но я полагаю, что должен быть более красноречивый способ заставить Vue.js реагировать, когда дело доходит до объектов, помещаемых в конец объекта. подмассивы.

Кто-нибудь знает, как лучше попытаться сделать то, чего я пытаюсь достичь? Спасибо.


person HartleySan    schedule 17.05.2019    source источник


Ответы (2)


Каждый раз, когда вы добавляете новое свойство к объекту или изменяете значение в массиве, вам необходимо использовать _ 1_.

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

В идеале вы должны определить все свои свойства заранее, чтобы Vue не аннулировал вычисленные свойства в зависимости от формы вашей модели данных. Даже если они установлены на null, вы должны попытаться отобразить все свойства, которые, по вашему мнению, могут понадобиться вашему компоненту.

Кроме того, в вашем первом блоке кода у вас есть двоеточие после вашего return:, которое будет оценивать как метку, что означает, что ваша функция данных ничего не возвращает.

person matpie    schedule 17.05.2019
comment
Двоеточие было ошибкой. Я отредактировал свой пост. Позвольте мне попробовать Vue.set, и я сообщу. Спасибо. - person HartleySan; 17.05.2019
comment
Мэтпи, у меня ушло день или два, чтобы вернуться к этому, но в итоге я последовал твоему совету и заранее определил все свойства. По сути, я перебирал переданную опору config и динамически создавал объект, возвращаемый функцией data, чтобы получить то, что я хотел. В результате мне пришлось использовать синтаксис $data['set' + num] в шаблоне для динамической ссылки на свойства, но он работает. Спасибо. - person HartleySan; 20.05.2019
comment
Рад помочь, @HartleySan! - person matpie; 20.05.2019

Вы можете попробовать что-то подобное, используя lodash и deep watcher ..

Подробнее deep watcher здесь

new Vue({
  el: "#app",
  methods: {
    setValue() {
      this.oldPeople = _.cloneDeep(this.people);
    }
  },
  mounted() {
    this.setValue();
  },
  el: "#app",
  data: {
    changed: null,
    people: [
      { id: 0, name: "Bob", age: 27 },
      { id: 1, name: "Frank", age: 32 },
      { id: 2, name: "Joe", age: 38 }
    ],
    oldPeople: []
  },
  watch: {
    people: {
      deep: true,
      handler(after, before) {
        // Return the object that changed
        let vm = this;
        let changed = after.filter(function(p, idx) {
          return Object.keys(p).some(function(prop) {
            return p[prop] !== vm.oldPeople[idx][prop];
          });
        });
        vm.setValue();
        this.changed = changed;
      },      
    }
  }
});
input {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.6/vue.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js"></script>

<div id="app">
  <div>
    <input type="text" v-for="(person, index) in people" v-model="people[index].age" />
    <div v-if="changed !== null">
      You changed:<br/>{{ changed }}
    </div>
  </div>
</div>

person Matt Oestreich    schedule 17.05.2019