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