| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- // 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
- // https://v2.vuejs.org/v2/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.
- // https://v2.vuejs.org/v2/guide/custom-directive.html
- directives: {
- 'todo-focus': function (el, binding) {
- if (binding.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')
|