app.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // Full spec-compliant TodoMVC with localStorage persistence
  2. // and hash-based routing in ~150 lines.
  3. // localStorage persistence
  4. var STORAGE_KEY = 'todos-vuejs-2.0'
  5. var todoStorage = {
  6. fetch: function () {
  7. var todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
  8. todos.forEach(function (todo, index) {
  9. todo.id = index
  10. })
  11. todoStorage.uid = todos.length
  12. return todos
  13. },
  14. save: function (todos) {
  15. localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
  16. }
  17. }
  18. // visibility filters
  19. var filters = {
  20. all: function (todos) {
  21. return todos
  22. },
  23. active: function (todos) {
  24. return todos.filter(function (todo) {
  25. return !todo.completed
  26. })
  27. },
  28. completed: function (todos) {
  29. return todos.filter(function (todo) {
  30. return todo.completed
  31. })
  32. }
  33. }
  34. // app Vue instance
  35. var app = new Vue({
  36. // app initial state
  37. data: {
  38. todos: todoStorage.fetch(),
  39. newTodo: '',
  40. editedTodo: null,
  41. visibility: 'all'
  42. },
  43. // watch todos change for localStorage persistence
  44. watch: {
  45. todos: {
  46. handler: function (todos) {
  47. todoStorage.save(todos)
  48. },
  49. deep: true
  50. }
  51. },
  52. // computed properties
  53. // https://v2.vuejs.org/v2/guide/computed.html
  54. computed: {
  55. filteredTodos: function () {
  56. return filters[this.visibility](this.todos)
  57. },
  58. remaining: function () {
  59. return filters.active(this.todos).length
  60. },
  61. allDone: {
  62. get: function () {
  63. return this.remaining === 0
  64. },
  65. set: function (value) {
  66. this.todos.forEach(function (todo) {
  67. todo.completed = value
  68. })
  69. }
  70. }
  71. },
  72. filters: {
  73. pluralize: function (n) {
  74. return n === 1 ? 'item' : 'items'
  75. }
  76. },
  77. // methods that implement data logic.
  78. // note there's no DOM manipulation here at all.
  79. methods: {
  80. addTodo: function () {
  81. var value = this.newTodo && this.newTodo.trim()
  82. if (!value) {
  83. return
  84. }
  85. this.todos.push({
  86. id: todoStorage.uid++,
  87. title: value,
  88. completed: false
  89. })
  90. this.newTodo = ''
  91. },
  92. removeTodo: function (todo) {
  93. this.todos.splice(this.todos.indexOf(todo), 1)
  94. },
  95. editTodo: function (todo) {
  96. this.beforeEditCache = todo.title
  97. this.editedTodo = todo
  98. },
  99. doneEdit: function (todo) {
  100. if (!this.editedTodo) {
  101. return
  102. }
  103. this.editedTodo = null
  104. todo.title = todo.title.trim()
  105. if (!todo.title) {
  106. this.removeTodo(todo)
  107. }
  108. },
  109. cancelEdit: function (todo) {
  110. this.editedTodo = null
  111. todo.title = this.beforeEditCache
  112. },
  113. removeCompleted: function () {
  114. this.todos = filters.active(this.todos)
  115. }
  116. },
  117. // a custom directive to wait for the DOM to be updated
  118. // before focusing on the input field.
  119. // https://v2.vuejs.org/v2/guide/custom-directive.html
  120. directives: {
  121. 'todo-focus': function (el, binding) {
  122. if (binding.value) {
  123. el.focus()
  124. }
  125. }
  126. }
  127. })
  128. // handle routing
  129. function onHashChange () {
  130. var visibility = window.location.hash.replace(/#\/?/, '')
  131. if (filters[visibility]) {
  132. app.visibility = visibility
  133. } else {
  134. window.location.hash = ''
  135. app.visibility = 'all'
  136. }
  137. }
  138. window.addEventListener('hashchange', onHashChange)
  139. onHashChange()
  140. // mount
  141. app.$mount('.todoapp')