1、HTML
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1"> 6 <title>Template • TodoMVC</title> 7 <link rel="stylesheet" href="node_modules/todomvc-app-css/index.css"> 8 <!-- CSS overrides - remove if you don't need it --> 9 <link rel="stylesheet" href="css/app.css"> 10 </head> 11 <body ng-app="MyTodoMvc"> 12 <section class="todoapp" ng-controller="MainController"> 13 <header class="header"> 14 <h1>todos</h1> 15 <form ng-submit="add()"> 16 <input class="new-todo" placeholder="What needs to be done?" ng-model="text" autofocus> 17 </form> 18 </header> 19 <section class="main"> 20 <input class="toggle-all" type="checkbox" ng-click="toggleAll()"> 21 <label for="toggle-all">Mark all as complete</label> 22 <ul class="todo-list"> 23 <li ng-repeat="todo in todos | filter: selector : equalCompare" ng-class="{completed:todo.completed,editing:todo.id===currentEditingId}" data-id="{{todo.id}}"> 24 <div class="view"> 25 <input class="toggle" type="checkbox" ng-model="todo.completed"> 26 <label ng-dblclick="editing(todo.id)">{{todo.text}}</label> 27 <button class="destroy" ng-click="remove(todo.id)"></button> 28 </div> 29 <form ng-submit="save()"> 30 <input class="edit" ng-model="todo.text" ng-blur="save()"> 31 </form> 32 </li> 33 </ul> 34 </section> 35 <!-- This footer should hidden by default and shown when there are todos --> 36 <footer class="footer"> 37 <!-- This should be `0 items left` by default --> 38 <span class="todo-count"><strong>{{todos.length}}</strong> item left</span> 39 <!-- Remove this if you don't implement routing --> 40 <ul class="filters"> 41 <li> 42 <a ng-class="{selected:$location.path() == '/'}" href="#/">All</a> 43 </li> 44 <li> 45 <a ng-class="{selected:$location.path() == '/active'}" href="#/active">Active</a> 46 </li> 47 <li> 48 <a ng-class="{selected:$location.path() == '/completed'}" href="#/completed">Completed</a> 49 </li> 50 </ul> 51 <!-- Hidden if no completed items are left ↓ --> 52 <button class="clear-completed" ng-click="clear()" ng-show="existCompleted()">Clear completed</button> 53 </footer> 54 </section> 55 <footer class="info"> 56 <p>Double-click to edit a todo</p> 57 <!-- Remove the below line ↓ --> 58 <p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p> 59 <!-- Change this out with your name and url ↓ --> 60 <p>Created by <a href="http://todomvc.com">you</a></p> 61 <p>Part of <a href="http://todomvc.com">TodoMVC</a></p> 62 </footer> 63 <!-- Scripts here. Don't remove ↓ --> 64 <script src="node_modules/angular/angular.js"></script> 65 <script src="js/app.js"></script> 66 </body> 67 </html>
2、app.js
1 (function(angular) { 2 'use strict'; 3 4 5 // 1. 为应用程序创建一个模块,用来管理界面的结构 6 var myApp = angular.module('MyTodoMvc', []); 7 8 // 2. 注册一个主要的控制器(属于某个模块),用于往视图(view)中暴露数据 9 myApp.controller('MainController', ['$scope', '$location', function($scope, $location) { 10 // [1,2,3,4,5] 11 // 获取唯一ID 12 function getId() { 13 var id = Math.random(); // 1 2 14 for (var i = 0; i < $scope.todos.length; i++) { 15 if ($scope.todos[i].id === id) { 16 id = getId(); 17 break; 18 } 19 } 20 return id; 21 } 22 23 // 文本框需要一个模型,为了拿到文本输入的值 24 $scope.text = ''; 25 26 // 任务列表也需要一个 27 // 每一个任务的结构 { id: 1, text: '学习', completed: true } 28 $scope.todos = [{ 29 id: 0.123, 30 text: '学习', 31 completed: false 32 }, { 33 id: 0.22, 34 text: '睡觉', 35 completed: false 36 }, { 37 id: 0.232, 38 text: '打豆豆', 39 completed: true 40 }]; 41 42 // 添加todo 43 $scope.add = function() { 44 if (!$scope.text) { 45 return; 46 } 47 $scope.todos.push({ 48 // 自动增长? 49 id: getId(), 50 // 由于$scope.text是双向绑定的,add同时肯定可以同他拿到界面上的输入 51 text: $scope.text, 52 completed: false 53 }); 54 // 清空文本框 55 $scope.text = ''; 56 }; 57 58 59 // 处理删除 60 $scope.remove = function(id) { 61 // 删除谁 62 for (var i = 0; i < $scope.todos.length; i++) { 63 if ($scope.todos[i].id === id) { 64 $scope.todos.splice(i, 1); 65 break; 66 } 67 } 68 // $scope.todos 69 }; 70 71 // 清空已完成 72 $scope.clear = function() { 73 var result = []; 74 for (var i = 0; i < $scope.todos.length; i++) { 75 if (!$scope.todos[i].completed) { 76 result.push($scope.todos[i]); 77 } 78 } 79 $scope.todos = result; 80 }; 81 82 // 是否有已经完成的 83 $scope.existCompleted = function() { 84 // 该函数一定要有返回值 85 for (var i = 0; i < $scope.todos.length; i++) { 86 if ($scope.todos[i].completed) { 87 return true; 88 } 89 } 90 return false; 91 }; 92 93 // 当前编辑哪个元素 94 $scope.currentEditingId = -1; 95 // -1代表一个肯定不存在的元素,默认没有任何被编辑 96 $scope.editing = function(id) { 97 $scope.currentEditingId = id; 98 }; 99 $scope.save = function() { 100 $scope.currentEditingId = -1; 101 }; 102 103 // $scope.checkall = false; 104 // $scope.$watch('checkall', function(now, old) { 105 // for (var i = 0; i < $scope.todos.length; i++) { 106 // $scope.todos[i].completed = now; 107 // } 108 // }); 109 110 var now = true; 111 $scope.toggleAll = function() { 112 for (var i = 0; i < $scope.todos.length; i++) { 113 $scope.todos[i].completed = now; 114 } 115 now = !now; 116 } 117 118 // 状态筛选 119 $scope.selector = {}; // {} {completed:true} {completed:false} 120 // 点击事件的方式不合适,有DOM操作 121 // 让$scope也有一个指向$location的数据成员 122 $scope.$location = $location; 123 // watch只能监视属于$scope的成员 124 $scope.$watch('$location.path()', function(now, old) { 125 // 1. 拿到锚点值 126 // 这样写就要求执行环境必须要有window对象 127 // var hash = window.location.hash; 128 // console.log($location); 129 // console.log(now); 130 // 2. 根据锚地值对selector做变换 131 switch (now) { 132 case '/active': 133 $scope.selector = { completed: false }; 134 break; 135 case '/completed': 136 $scope.selector = { completed: true }; 137 break; 138 default: 139 $scope.selector = {}; 140 break; 141 } 142 }); 143 144 // 自定义比较函数, 默认filter过滤器使用的是模糊匹配 145 $scope.equalCompare = function(source, target) { 146 // console.log(source); 147 // console.log(target); 148 // return false; 149 return source === target; 150 }; 151 152 153 }]); 154 155 })(angular);