I'm currently trying to teach myself Vue.js and I've just created my first small project: a simple todo list. While writing the component I discovered that you can't access methods from the parent component directly, but I didn't want to put one half of the code in the child component (the deleteToDo
method), so I've passed a callback to it. Is this okay in Vue or is there any other preferred way of structuring things like this?
Vue.component('todo-item', {
props: ['todo', 'delete_callback'],
template: `<li>
<input type="checkbox" v-model="todo.done">
<input v-model="todo.text" :disabled="todo.done" @change="todoChanged(todo)">
<a @click="delete_callback(todo)">X</a>
</li>`,
methods: {
todoChanged: function (todo) {
if (this.todo.text == '') {
this.delete_callback(todo);
}
}
}
});
var vm = new Vue({
el: '#app',
data: {
show: {
todo: true,
done: false
},
todos: [
{
id: 0,
text: 'Test this stuff',
done: true
},
{
id: 1,
text: 'Learn more Vue',
done: false
},
{
id: 2,
text: 'Buy some tasty food',
done: false
}
]
},
methods: {
addTodo: function (event) {
if (event instanceof KeyboardEvent && event.key != 'Enter') {
return;
}
if (event.target.value != '') {
this.todos.push({
id: this.todos.length,
text: event.target.value,
done: false
});
event.target.value = '';
}
},
deleteToDo: function (todo) {
this.todos.splice(todo.id, 1);
for (let i = 0; i < this.todos.length; i++) {
this.todos[i].id = i;
}
}
}
})
* {
box-sizing: border-box;
}
body {
font: 16px "Helvetica Neue", status-bar;
background-color: #444444;
}
#app {
max-width: 400px;
margin: 3rem auto;
padding: 1rem 3rem 3rem 3rem;
background-color: #dddddd;
color: #444444;
}
li {
list-style: none;
}
li > input:disabled {
text-decoration: line-through;
}
li > a, h4 > span {
font-size: 0.7rem;
}
li > a:hover {
cursor: pointer;
color: #222222;
}
#newtodo {
margin-left: 23px;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>ToDo</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link rel="stylesheet" href="test.css">
</head>
<body>
<div id="app">
<h1>ToDo List</h1>
<h4 @click="show.todo = !show.todo">To Do: {{ todos.filter(todo => !todo.done).length }} <span>({{ show.todo ? 'Hide' : 'Show' }})</span></h4>
<ul v-show="show.todo">
<todo-item v-for="todo in todos" v-if="!todo.done" :todo="todo" :key="todo.id" :delete_callback="deleteToDo"></todo-item>
<input id="newtodo" type="text" placeholder="New todo item" @keypress="addTodo" @blur="addTodo"></input>
</ul>
<h4 @click="show.done = !show.done">Done: {{ todos.filter(todo => todo.done).length }} <span>({{ show.done ? 'Hide' : 'Show' }})</span></h4>
<ul v-show="show.done">
<todo-item v-for="todo in todos" v-if="todo.done" :todo="todo" :key="todo.id" :delete_callback="deleteToDo"></todo-item>
</ul>
</div>
<script src="test.js"></script>
</body>
</html>