0

In my VueJS 2 component below, I can add the imgdata property to each question in the area.questions array. It works - I can see from the console.log that there are questions where imgdata has a value. But despite using $set it still isn't reactive, and the imgdata isn't there in the view! How can I make this reactive?

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);
    },
4
  • 1
    Do not mutate prop values. See vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow Commented Oct 7, 2020 at 3:49
  • Props can't be a reactive. Let me know if you still looking help Commented Oct 7, 2020 at 4:02
  • @PunitPatel props are most definitely reactive Commented Oct 7, 2020 at 4:24
  • @Phil Yes, Phil you are right it's can't mutate from component. To change it we should use $emit. Commented Oct 7, 2020 at 4:29

2 Answers 2

2

Since area is a prop, you should not be attempting to make changes to it within this component.

The general idea is to emit an event for the parent component to listen to in order to update the data passed in.

For example

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

And in the parent

<image-loader :area="area" @loaded="updateAreaQuestions"/>
export default {
  data: () => ({
    area: {
      questions: [/* questions go here */]
    }
  }),
  methods: {
    updateAreaQuestions(questions) {
      this.area.questions = questions
    }
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

(y) Perfect explanation :)
-1

Here that variable has a value of this but it's bound under the scope of function. So, you can create reactive property in data as below :

data: function() {
    return {
        qIndex: 0,
        questions: []
    };
}
Props can't be reactive so use :

that.$set(this.questions, thisIndex, thisQuestion);

And assign your API output to directly questions using this.questions.

3 Comments

Thanks for your answer - I think it is helpful, but will uncouple the array of questions from the area object it belongs to. PS. I didn't downvote it - perhaps there is a way to incorporate this idea
Thanks @jdoer1997 I know many people here only for score and down voted. I don't care those peoples. I keep help people. Let me know if you still stuck.
@PunitPatel I have to disagree on this point. People are not here just for the score, you would only get downvoted if somebody thinks your answer isn't either good enough or is irrelevant.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.