diff --git a/examples/todomvc/app.js b/examples/todomvc/app.js new file mode 100644 index 00000000..aafbbda7 --- /dev/null +++ b/examples/todomvc/app.js @@ -0,0 +1,157 @@ +// Full spec-compliant TodoMVC with localStorage persistence +// and hash-based routing in ~150 lines. + +// localStorage persistence +var STORAGE_KEY = 'todos-vuejs-2.0' +var todoStorage = { + fetch: function () { + var todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]') + todos.forEach(function (todo, index) { + todo.id = index + }) + todoStorage.uid = todos.length + return todos + }, + save: function (todos) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)) + } +} + +// visibility filters +var filters = { + all: function (todos) { + return todos + }, + active: function (todos) { + return todos.filter(function (todo) { + return !todo.completed + }) + }, + completed: function (todos) { + return todos.filter(function (todo) { + return todo.completed + }) + } +} + +// app Vue instance +var app = new Vue({ + // app initial state + data: { + todos: todoStorage.fetch(), + newTodo: '', + editedTodo: null, + visibility: 'all' + }, + + // watch todos change for localStorage persistence + watch: { + todos: { + handler: function (todos) { + todoStorage.save(todos) + }, + deep: true + } + }, + + // computed properties + // http://vuejs.org/guide/computed.html + computed: { + filteredTodos: function () { + return filters[this.visibility](this.todos) + }, + remaining: function () { + return filters.active(this.todos).length + }, + allDone: { + get: function () { + return this.remaining === 0 + }, + set: function (value) { + this.todos.forEach(function (todo) { + todo.completed = value + }) + } + } + }, + + filters: { + pluralize: function (n) { + return n === 1 ? 'item' : 'items' + } + }, + + // methods that implement data logic. + // note there's no DOM manipulation here at all. + methods: { + addTodo: function () { + var value = this.newTodo && this.newTodo.trim() + if (!value) { + return + } + this.todos.push({ + id: todoStorage.uid++, + title: value, + completed: false + }) + this.newTodo = '' + }, + + removeTodo: function (todo) { + this.todos.splice(this.todos.indexOf(todo), 1) + }, + + editTodo: function (todo) { + this.beforeEditCache = todo.title + this.editedTodo = todo + }, + + doneEdit: function (todo) { + if (!this.editedTodo) { + return + } + this.editedTodo = null + todo.title = todo.title.trim() + if (!todo.title) { + this.removeTodo(todo) + } + }, + + cancelEdit: function (todo) { + this.editedTodo = null + todo.title = this.beforeEditCache + }, + + removeCompleted: function () { + this.todos = filters.active(this.todos) + } + }, + + // a custom directive to wait for the DOM to be updated + // before focusing on the input field. + // http://vuejs.org/guide/custom-directive.html + directives: { + 'todo-focus': function (el, value) { + if (value) { + el.focus() + } + } + } +}) + +// handle routing +function onHashChange () { + var visibility = window.location.hash.replace(/#\/?/, '') + if (filters[visibility]) { + app.visibility = visibility + } else { + window.location.hash = '' + app.visibility = 'all' + } +} + +window.addEventListener('hashchange', onHashChange) +onHashChange() + +// mount +app.$mount('.todoapp') diff --git a/examples/todomvc/index.html b/examples/todomvc/index.html index 1caa2778..063229bc 100644 --- a/examples/todomvc/index.html +++ b/examples/todomvc/index.html @@ -58,12 +58,11 @@
Part of TodoMVC
- - - - + +