[关闭]
@xiaoqq 2016-08-03T12:12:57.000000Z 字数 5944 阅读 1005

AngularJS学习笔记——ToDoMVC

AngularJS


一、 引言

ToDoMVC是采用很多套MV* 框架来实现来实现一个固定规范的JavaScript Web App,从而方便我们对不同的框架进行学习和比较。

二、 具体需求与实现:

No todos

When there are no todos, #main and #footer should be hidden.

New todo

New todos are entered in the input at the top of the app. The input element should be focused when the page is loaded, preferably by using the autofocus input attribute. Pressing Enter creates the todo, appends it to the todo list, and clears the input. Make sure to .trim() the input and then check that it's not empty before creating a new todo.

在新增任务的表单中增加ng-submit="addTodo()",并且在$scope中绑定函数

  1. <!-- 新增任务 -->
  2. <form id="todo-form" ng-submit="addTodo()">
  3. <input type="text" id="new-todo" placeholder="待办事项" ng-model="newTodo" autofocus>
  4. </form>
  5. //增加任务
  6. $scope.addTodo = function() {
  7. var newTodo = {
  8. title: $scope.newTodo.trim(),
  9. completed: false
  10. };
  11. if (newTodo.title == "") {
  12. return;
  13. }
  14. $scope.saving = true;
  15. store.insert(newTodo).then(function() {
  16. $scope.newTodo = "";
  17. console.log(arguments[0]);
  18. return arguments[0];
  19. }).finally(function() {
  20. $scope.saving = false;
  21. });
  22. };

Mark all as complete

This checkbox toggles all the todos to the same state as itself. Make sure to clear the checked state after the "Clear completed" button is clicked. The "Mark all as complete" checkbox should also be updated when single todo items are checked/unchecked. Eg. When all the todos are checked it should also get checked.

在checkbox中绑定ng-model="allChecked",并且绑定click函数ng-click="markAll(allChecked)"

  1. <!-- 全选/全不选 任务 -->
  2. <input type="checkbox" id="toggle-all" ng-click="markAll(allChecked)" ng-model="allChecked">
  3. $scope.markAll = function(allChecked) {
  4. var length = todos.length;
  5. for (var i = 0; i < length; i++) {
  6. todos[i].completed = allChecked;
  7. store.put(todos[i],i);
  8. }
  9. };

Item

A todo item has three possible interactions:

  1. Clicking the checkbox marks the todo as complete by updating its completed value and toggling the class completed on its parent <li>

  2. Double-clicking the <label> activates editing mode, by toggling the .editing class on its <li>

  3. Hovering over the todo shows the remove button (.destroy)

  1. <li ng-repeat="todo in todos track by $index" ng-class="{completed: todo.completed, editing:todo == editTodo}">
  2. <div class="view">
  3. <input type="checkbox" class="toggle" ng-model="todo.completed">
  4. <label ng-dblclick="showEdit(todo)">{{todo.title}}</label>
  5. <!-- 删除任务 -->
  6. <button class="destroy" ng-click="deleteTodo($index)"></button>
  7. </div>
  8. <!-- 编辑任务 -->
  9. <form>
  10. <input type="text" class="edit">
  11. </form>
  12. </li>
  13. $scope.deleteTodo = function(index) {
  14. store.delete(index);
  15. };
  16. $scope.showEdit = function (todo) {
  17. $scope.editTodo = todo;
  18. }

Editing

When editing mode is activated it will hide the other controls and bring forward an input that contains the todo title, which should be focused (.focus()). The edit should be saved on both blur and enter, and the editing class should be removed. Make sure to .trim() the input and then check that it's not empty. If it's empty the todo should instead be destroyed. If escape is pressed during the edit, the edit state should be left and any changes be discarded.

双击后input自动获取焦点:

定义一个指令

  1. <input type="text" class="edit" ng-blur="saveEdit(todo)" ng-model = "todo.title" index="{{$index}}" todo-focus = "todo === editTodo" todo-escape="revertEdit(todo)">
  2. todomvc.directive('todoFocus', function($timeout) {
  3. // Runs during compile
  4. return function($scope, iElm, iAttrs, controller) {
  5. $scope.$watch(iAttrs.todoFocus, function(isEdit) {
  6. if (isEdit) {
  7. //iElm[0].focus();
  8. $timeout(function() {
  9. iElm[0].focus();
  10. }, 0);
  11. }
  12. })
  13. };
  14. });

但是不明白,为什么focus事件需要放在$timeout中?

按住ESC键撤销编辑:

  1. //指令监听ESC按键
  2. todomvc.directive('todoEscape', function() {
  3. var ESCAPE_KEY = 27;
  4. return function($scope, elem, attrs) {
  5. elem.bind('keydown',function(event){
  6. if(event.keyCode === ESCAPE_KEY){
  7. $scope.$apply(attrs.todoEscape);
  8. }
  9. });
  10. }
  11. });
  12. $scope.revertEdit = function(todo){
  13. angular.extend(todo, $scope.orginTodo);
  14. $scope.editTodo = null;
  15. };

Counter

Displays the number of active todos in a pluralized form. Make sure the number is wrapped by a <strong> tag. Also make sure to pluralize the item word correctly: 0 items, 1 item, 2 items. Example: 2 items left

Clear completed button

Removes completed todos when clicked. Should be hidden when there are no completed todos.
清除完成项:

  1. //清除完成任务
  2. clearCompleted: function() {
  3. var deferred = $q.defer();
  4. var incompleteTodos = store.todos.filter(function(todo){
  5. return !todo.completed;
  6. });
  7. angular.copy(incompleteTodos,store.todos);
  8. store._saveLocalStorage(store.todos);
  9. return deferred.promise;
  10. },

重点在于使用filter过滤出未完成的list

Persistence

Your app should dynamically persist the todos to localStorage. If the framework has capabilities for persisting data (e.g. Backbone.sync), use that. Otherwise, use vanilla localStorage. If possible, use the keys id, title, completed for each item. Make sure to use this format for the localStorage name: todos-[framework]. Editing mode should not be persisted.

把调用localStorage的功能写在服务中,主要包含增删改查等功能。

  1. //注册服务
  2. todomvc.factory('localStorage', ['$q', function($q) {
  3. var STORAGE_ID = 'myStorage';
  4. ...
  5. }]);

页面加载时从localStorage中读取事项:

  1. var todomvc = angular.module('todomvc', ['ngRoute', 'ngResource'])
  2. .config(['$routeProvider', function($routeProvider) {
  3. var config = {
  4. templateUrl: "todomvc-index.html",
  5. controller: "TodoCtrl",
  6. resolve: {
  7. store: function(localStorage) {
  8. localStorage.get();
  9. return localStorage;
  10. }
  11. }
  12. };
  13. $routeProvider.when('/', config)
  14. .when("/:status", config);
  15. }]);

Routing

Routing is required for all implementations. If supported by the framework, use its built-in capabilities. Otherwise, use the Flatiron Director routing library located in the /assets folder. The following routes should be implemented: #/ (all - default), #/active and #/completed (#!/ is also allowed). When the route changes, the todo list should be filtered on a model level and the selected class on the filter links should be toggled. When an item is updated while in a filtered state, it should be updated accordingly. E.g. if the filter is Active and the item is checked, it should be hidden. Make sure the active filter is persisted on reload.

三、 项目地址

代码地址:https://github.com/xiaoqqchen/xiaoqqchen.github.io/tree/master/AngularJS/demo2
演示地址:http://xiaoqqchen.github.io/AngularJS/demo2/#/

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注