Reputation: 487
I've started to use Vuex in order to remplace the EventBus, because the data in my app has started to get a little bit complex.
In my context, I have a question entity, with multiple answers, when the user insert another answer I want to show the last one; (here I use two different components: one to show the answers and other to answer the question) but when the server response OK with the new answer, and the mutation change the state.answers, the computed property doesn't react and doesn't show the new answer:
Here is my data structure:
"answers": {
"118": {
"id": 118,
"description": "objective",
"created_at": "2019-11-12T19:12:36.015Z",
"dojo_process_id": 1,
"question_id": 1,
"user_id": 10
}
"127": {
"id": 127,
"description": "asdddd",
"created_at": "2019-11-12T19:38:19.233Z",
"dojo_process_id": 1,
"question_id": 1,
"user_id": 10
},
"128": {
"id": 128,
"description": "asddddasddd",
"created_at": "2019-11-12T20:00:17.572Z",
"dojo_process_id": 1,
"question_id": 1,
"user_id": 10
}
},
Here is the code for my store:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
...
answers: {},
...
},
getters: {
findBy: state=> filter => {
let result= Object.values(state[filter.model]).
filter(data => data[filter.field] === filter.criteria);
return result;
}
},
mutations: {
setAnswers(state, answers) {
state.answers = answers;
},
setAnswer(state, answer) {
state.answers[answer.id] = answer;
},
},
actions: {
replaceCompleteProcess(context, data) {
...
context.commit('setAnswers',data.answers);
...
},
cleanCompleteProcess(context) {
...
context.commit('setAnswers',{});
...
},
saveAnswer(context, answer) {
context.commit('setAnswer', answer);
}
}
});
And this is how the script of my component is structured:
export default {
name: "show-question",
computed: {
question: function () {
return this.$store.getters.question(this.questionId)
},
answers: function () {
return this.$store.getters.findBy({
model: 'answers',
field: 'question_id',
criteria: this.questionId,
sort: true
});
},
answer: function () {
if (this.answers && this.answers.length > 0) {
return this.answers[0].description;
} else {
return '';
}
}
},
props: {
questionId: {
type: [Number],
default: 0
}
},
data() {
return {
sending: false,
answerData: this.answer
}
},
methods: {
sendAnswer () {
this.sending = true;
questionConnector.answerQuestion(this,this.question.id,this.dojoProcessId, this.answerData)
},
// this one is called from AXIOS
answerWasOK(answer) {
this.sending = false;
this.$store.dispatch('saveAnswer', answer);
this.answerData = '';
}
}
}
So, if I understand how to use Vuex, when I call this.$store.dispatch('saveAnswer', answer), the state will be updated, and the computed property answers would be updated, and I'll be able to show the new changes in the component, But it doesn't work.... the computed property just doesn't react.
I had read a lot about vuex and how "it not work well" with complex data, so I normalize my data. but it is the same... also I tried to use vuex-orm, but I have a lot of problems with the one-many relation, and I cant do it work.
EDIT: Solution
I did a small test with the ideas from the answers and it works
setAnswer(state, answer) {
let newAnswers = state.answers;
state.answers = {};
newAnswers[answer.id] = answer;
state.answers = newAnswers;
}
Upvotes: 4
Views: 1393
Reputation: 709
You are storing a list of answers inside an object. Nothing wrong with that, since you know how to deal with. It turns out that Vue.js observers
don't track new object attributes (which is exactly what you are doing there, creating new attributes, instead of modifying a list/array).
My first suggestion is to change this object to an array. But if you can't, due to your ORM or other reason of your project standard, you should take a look about Reactivity
of Vue.js
. The quickest solution, in my opinion, is to use a watcher
:
https://v2.vuejs.org/v2/api/#watch
Some links that can be helpful to understand Vue.js reactivity:
Reactivity in Depth
https://v2.vuejs.org/v2/guide/reactivity.html
How to actively track an object property change?
https://forum.vuejs.org/t/how-to-actively-track-an-object-property-change/34402/1
Upvotes: 1
Reputation: 1058
When you are working with Objects you have to do it like this
setAnswer(state, answer) {
Vue.set(state.answers, answer.id, answer);
},
This is clearly mentioned in the documentation.
When adding new properties to an Object, you should either:
- Use Vue.set(obj, 'newProp', 123), or
- Replace that Object with a fresh one. For example, using the object spread syntax we can write it like this:
state.obj = { ...state.obj, newProp: 123 }
Upvotes: 6