Vue.js ToDo

by samonela

HTML

<!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" />
            </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>

CSS

* {
    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;
}

JavaScript

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