I'm building a todo app. There is a button to toggle the checked/completed state of all todos. I'm mutating an array of the state
, but the getters
are not being updating. When I click toggle complete
button, the database and the array state.todos
update, but the $store.getters.filteredTodos
does not update.
So the some todos are displayed incorrectly (some are checked and others are not when all should be checked or unchecked)
// in getters
filteredTodos(state) {
if (state.filter == 'active') {
return state.todos.filter(todo => !todo.completed);
}
if (state.filter == 'completed') {
return state.todos.filter(todo => todo.completed);
}
return state.todos;
},
// in mutations
retrieveTodos(state, todos) {
Vue.set(state, 'todos', todos);
// tried the following, but also didn't work
//state.todos.splice(0, state.todos.length, ...todos);
},
// in actions
toggleCompleted(context, completed) {
axios.patch('/todos/toggle-completed', {completed})
// response.data is the array of the updated todo items
.then(response => context.commit('retrieveTodos', response.data))
.catch(e => console.log(e));
},
From several posts here in StackOverflow and in VueForum, I have seen the same solution:
Vue.set(state, 'todos', todos);
// or
state.todos.splice(0, state.todos.length, ...todos);
None of them worked. The $store.state
updates, but the $store.getters.filteredTodos
does not.
How to solve this?
UPDATE
I'm following this tutorial series on YouTube to get started with Vue, but I'm doing somethings on my own. In the tutorial, the guy creates the Vue project for the fronend and a Laravel project for the backend. I did the same.
I discovered that the problem had nothing to do with getters
or state
. Both were being updated correctly in the three ways that I tried.
The problem was in the Todo
component. Previously, the code was something like this:
// in the template
<label class="todo-action" :for="`todo-checked-${id}`">
<input type="checkbox" v-model="completed"
:id="`todo-checked-${id}`" @change="update">
</label>
<div class="todo-title">
{{title}}
</div>
// inside script tag
data() {
// this allow me to access this.id, this.completed, and this.title
// instead of this.todo.id, this.todo.completed, and this.todo.title
return this.todo;
},
The problem is that data
was not being updated. I fixed it as follows
// in the template I access the props from the todo item, so it is always up to date
<label class="todo-action" :for="`todo-checked-${todo.id}`">
<input type="checkbox" v-model="todo.completed"
:id="`todo-checked-${todo.id}`" @change="update">
</label>
<div class="todo-title">
{{todo.title}}
</div>
Yeah, it was a very simple solution