VueJS $set не делает новое свойство в массиве объектов реактивным

#vue.js #vuejs2

#vue.js #vuejs2

Вопрос:

В моем компоненте VueJS 2 ниже я могу добавить свойство imgdata к каждому вопросу в этой области.массив вопросов. Это работает — я вижу из консоли.запишите, что есть вопросы, в которых imgdata имеет значение. Но, несмотря на использование $set, оно по-прежнему не реагирует, и imgdata отсутствует в представлении! Как я могу сделать это реактивным?

 var componentOptions = {
  props: ['area'],
  data: function() {
    return {
      qIndex: 0,
    };
  },
  mounted: function() {
    var that = this;
    that.init();
  },
  methods: {
    init: function() {
      var that = this;
      if (that.area.questions.length > 0) {
        that.area.questions.forEach(function(q) {
          Util.HTTP('GET', '/api/v1/photos/'   q.id   '/qimage').then(function(response) {
            var thisIndex = (that.area.questions.findIndex(entry => entry.id === q.id));
            var thisQuestion = (that.area.questions.find(entry => entry.id === q.id));
            thisQuestion.imgdata = response.data;
            that.$set(that.area.questions, thisIndex, thisQuestion);
          })
        });
      }
      console.log("area.questions", that.area.questions);
    },
  

Комментарии:

1. Не изменяйте значения реквизитов. Смотрите vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow

2. Реквизит не может быть реактивным. Дайте мне знать, если вы все еще ищете помощь

3. Реквизиты @PunitPatel, безусловно, реактивные

4. @Phil Да, Фил, ты прав, он не может мутировать из компонента. Чтобы изменить его, мы должны использовать $emit .

Ответ №1:

Поскольку area это prop, вы не должны пытаться вносить в него изменения в этом компоненте.

Общая идея состоит в том, чтобы выдать событие для прослушивания родительским компонентом, чтобы обновить переданные данные.

Например

 export default {
  name: "ImageLoader",
  props: {
    area: Object
  },
  data: () => ({ qIndex: 0 }), // are you actually using this?
  mounted () {
    this.init()
  },
  methods: {
    async init () {
      const questions = await Promise.all(this.area.questions.map(async q => {
        const res = await Util.HTTP("GET", `/api/v1/photos/${encodeURIComponent(q.id)}/qimage`)
        return {
          ...q,
          imgdata: res.data
        }
      }))
      this.$emit("loaded", questions)
    }
  }
}
  

И в родительском

 <image-loader :area="area" @loaded="updateAreaQuestions"/>
  
 export default {
  data: () => ({
    area: {
      questions: [/* questions go here */]
    }
  }),
  methods: {
    updateAreaQuestions(questions) {
      this.area.questions = questions
    }
  }
}
  

Комментарии:

1. (y) Идеальное объяснение 🙂

Ответ №2:

Здесь эта переменная имеет значение this, но она привязана к области действия функции. Итак, вы можете создать реактивное свойство в данных, как показано ниже :

 data: function() {
    return {
        qIndex: 0,
        questions: []
    };
}  

Реквизит не может быть реактивным, поэтому используйте :

это.$set(this.вопросы, thisIndex, thisQuestion);

И назначьте свой вывод API непосредственно вопросам, используя это.вопросы.

Комментарии:

1. Спасибо за ваш ответ — я думаю, что это полезно, но отсоединит массив вопросов от объекта area, к которому он принадлежит. PS. Я не отрицал это — возможно, есть способ включить эту идею

2. Спасибо @jdoer1997 Я знаю, что многие люди здесь проголосовали только за оценку и проголосовали против. Мне плевать на этих людей. Я продолжаю помогать людям. Дайте мне знать, если вы все еще застряли.

3. @PunitPatel Я должен не согласиться с этим. Люди здесь не только для оценки, вы получите отрицательный ответ, только если кто-то сочтет ваш ответ недостаточно хорошим или неуместным.