I'm trying to do a simple todo list in Vue but I want to abstract everything out and use a dummy REST API so I can start to get used to production-level projects in Vue and it's all making my head spin. GET, PUT, and POST requests seem to be working, but I can't figure out why the list of todos doesn't update automatically when I do a successful POST request to the back end.
I've got a TodoList
component that loops through a todosFiltered()
computed property to show the todos. The computed property refers back to the getter todosFiltered
in the Vuex store. I also use the created()
lifecycle hook here to dispatch an action in the store that makes the initial GET request and then populates an array called todos
in the store when the page is first loaded. The getter todosFiltered
in the store returns state.todos
, so I assumed that when my component re-renders, it would have the new todos
array from the state grabbed from todosFiltered
, only that's not happening. What am I missing here? Any advice would be greatly appreciated.
(I know I'll have to work out a solution for the ids, it's on my list :p)
<template>
<div class="container">
<input v-model="newTodo" type="text" placeholder="What must be done?" class="todo-input" @keyup.enter="addTodo">
<transition-group name="fade" enter-active-class="animated zoomIn" leave-active-class="animated zoomOut">
<todo-item v-for="todo in todosFiltered" :key="todo.id" :checkAll="!anyRemaining" :todo="todo"></todo-item>
</transition-group>
<div class="extra-container">
<todos-filtered></todos-filtered>
</div>
</div>
</template>
<script>
import TodosFiltered from './TodosFiltered'
import TodoItem from './TodoItem'
export default {
name: 'todolist',
components: {
TodosFiltered,
TodoItem
},
data() {
return {
beforeEditCache: '',
newTodo: '',
idForTodo: 10,
}
},
// Methods
methods: {
addTodo() {
if (this.newTodo.trim().length == 0) {
return
}
this.$store.dispatch('addTodo', {
id: this.idForTodo,
title: this.newTodo,
completed: false
})
this.newTodo = ''
this.idForTodo++
}
},
computed: {
todosFiltered() {
return this.$store.getters.todosFiltered
},
},
created() {
this.$store.dispatch('loadTodos')
},
}
</script>
export const store = new Vuex.Store({
state: {
filter: 'all',
todos: []
},
getters: {
todosFiltered(state) {
if (state.filter == 'all') {
return state.todos
} else if (state.filter == 'active') {
return state.todos.filter(todo => !todo.completed)
} else if (state.filter == 'completed') {
return state.todos.filter(todo => todo.completed)
}
return state.todos
},
showClearCompleted(state) {
return state.todos.filter(todo => todo.completed).length > 0
}
},
mutations: {
addTodo(state, todo) {
state.todos.push(todo)
},
setTodos(state, todos) {
state.todos = todos
},
},
actions: {
loadTodos(context) {
axios.get('http://localhost:3000/todos')
.then(r => r.data)
.then(todos => {
context.commit('setTodos', todos)
})
},
updateTodo(context, todo) {
axios.put('http://localhost:3000/todos/' + todo.id, {
"id": todo.id,
"title": todo.title,
"completed": todo.completed
})
},
addTodo(context, todo) {
axios.post('http://localhost:3000/todos', {
"id": todo.id,
"title": todo.title,
"completed": todo.completed
})
.then(todo => {
context.commit('addTodo', todo)
})
},
}
})
EDIT: Here's what's going on in Vue Dev Tools when I add a todo -- todos
in the store's state gets updated immediately, and the todosFiltered
computed property in the TodoList
component ALSO reflects that -- but the new todo doesn't appear in the list! Strange.
The problem is using a $store.getter on the v-for loop. Try the following:
Set your computed to
todos() { return this.$store.todos; }
Change your v-for loop to use todo in todos
Hope this works for you
A way to solve this can be to create what I like to call a refresh()
method.
Basically, you will have a local list of todos
in your data()
method, the refresh()
method will load all the todos
from the store into the local todos
list, every time you do an action, such as creating, deleting, or updating, you would call the refresh method to re-load the list for you.
So, in your TodoList.vue
:
<template>
<todo-item v-for="todo in todosFiltered" :key="todo.id"></todo-item>
</template>
<script>
export default {
data() {
return {
// Where we store the local list of Todos
// so the component will react when we do something to it
todosFiltered: []
}
},
methods {
refresh() {
// Get the todo list from the store.
// which in turn will trigger a change event so the component
// react to what we did.
this.todosFiltered = this.$store.getters.todosFiltered;
},
addTodo() {
this.$store.dispatch('addTodo').then(() => {
// Refresh the list after adding a new Todo
this.refresh();
})
},
updateTodo() {
this.$store.dispatch('updateTodo').then(() => {
// Refresh the list after updating a Todo
this.refresh();
})
},
deleteTodo() {
this.$store.dispatch('deleteTodo').then(() => {
// Refresh the list after deleting a Todo
this.refresh();
})
}
},
created() {
this.$store.dispatch('loadTodos').then( () => {
// Refresh the list when first loaded after the Todos been saved in Vuex
this.refresh();
})
}
}
</script>
Don't actually delete what you already have and replace it with this, just apply what's here to your code.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.