AngularJS学习笔记之TodoMVC的分析

最近一段时间一直在看AngularJS,趁着一点时间总结一下。

官网地址:

先推荐几个教程

1. 比较基础,是官方Tutorial的翻译。

2. 七步从AngularJS菜鸟到专家 也比较基础,制作了一个在线音乐播放网站。

3. 这个教程比较全面,但我感觉翻译的有些晦涩难懂。

看过这些教程后,觉得AngularJS也懂一点了,就想用它干点事,就分析一下AngularJS写的todomvc吧。

Todomvc官网地址:

项目的目录如下:

bower_components里放了两个文件夹,其中angular文件夹是用来一如angular.js文件的,todomvc-common文件夹里的放入了所有todo项目统一的css\js(只是用来生成左侧内容的,与项目无关)和图片

js文件夹是大头,里面放了相应的controller(控制器)\directive(指令)\service(服务)和app.js。

test文件夹里放的是测试用的代码,不分析。

index.html是项目的view页面

先来看一下app.js

代码如下:
odoMVC app module * * @type {angular.Module} */ var todomvc = angular.module('todomvc',[]);

就是定义了一个模块todomvc

再看一下services下的todoStorage.js

代码如下:
odomvc */ 'use strict'; /** * Services that persists and retrieves Todos from localStorage */ todomvc.factory('todoStorage',function () { // todos JSON字符串存储的唯一标识 var STORAGE_ID = 'todos-angularjs'; return { // 从localStorage中取出todos,并解析成JSON对象 get: function () { return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]'); }, // 将todos对象转化成JSON字符串,并存入localStorage put: function (todos) { localStorage.setItem(STORAGE_ID,JSON.stringify(todos)); } }; });

使用factory方法创建了todoStorage的service方法,这个service方法的本质就是返回了两个方法get和put,两者都是用了JSON2和HTML5的特性。get将todos的内容从localStorage中取出,并解析成JSON,put将todos转化成JSON字符串,并存储到localStorage中。

再看一下directives下面的两个指令文件

todoFocus.js

代码如下:
odomvc */ 'use strict'; /** * Directive that places focus on the element it is applied to when the expression it binds to evaluates to true */ todomvc.directive('todoFocus',function todoFocus($timeout) { return function (scope,elem,attrs) { // 为todoFocus属性的值添加监听 scope.$watch(attrs.todoFocus,function (newVal) { if (newVal) { $timeout(function () { elem[0].focus(); },false); } }); }; });

返回function的参数中,elem就是包含该指令的元素的数组,attrs是元素的所有属性属性名等组成的对象。

其中用到了两个AngularJS的方法

$watch(watchExpression,listener,objectEquality) 注册一个侦听器回调,每当watchExpression变化时,监听回调将被执行。

$timeout(fn[,delay][,invokeApply]) 当timeout的值达到时,执行fn函数

todoFocus.js创建了todoFocus指令。当一个元素拥有todoFocus属性时,该指令会为该元素的todoFocus属性的值添加监听,如果todoFocus属性的值改变成true,就会执行$timeout(function () {elem[0].focus();},false);其中的延迟时间为0秒,所以会立即执行elem[0].focus()。

todoEscape.js

代码如下:
odomvc */ 'use strict'; /** * Directive that executes an expression when the element it is applied to gets * an `escape` keydown event. */ todomvc.directive('todoEscape',function () { var ESCAPE_KEY = 27; return function (scope,attrs) { elem.bind('keydown',function (event) { if (event.keyCode === ESCAPE_KEY) { scope.$apply(attrs.todoEscape); } }); }; });

todoEscape.js创建了todoEscape指令。当按下Escape键时,执行attrs.todoEscape的表达式。

看一下大头,controllers文件夹中的todoCtrl.js,这个文件略长,我就直接写注释了。

代码如下:
odomvc,angular */ 'use strict'; /** * The main controller for the app. The controller: * - retrieves and persists the model via the todoStorage service * - exposes the model to the template and provides event handlers */ todomvc.controller('TodoCtrl',function TodoCtrl($scope,$location,todoStorage,filterFilter) { // 从localStorage中获取todos var todos = $scope.todos = todoStorage.get();

// 记录新的todo $scope.newTodo = ''; // 记录编辑过的todo $scope.editedTodo = null; // 当todos的值改变时执行其中的方法 $scope.$watch('todos',function (newValue,oldValue) { // 获取未完成的todos的数目 $scope.remainingCount = filterFilter(todos,{ completed: false }).length; // 获取已完成的todos的数目 $scope.completedCount = todos.length - $scope.remainingCount; // 当且仅当$scope.remainingCount为0时,$scope.allChecked为true $scope.allChecked = !$scope.remainingCount; // 当todos的新值和旧值不相等时,向localStorage中存入todos if (newValue !== oldValue) { // This prevents unneeded calls to the local storage todoStorage.put(todos); } },true); if ($location.path() === '') { // 如果$location.path()为空,就设置为/ $location.path('/'); } $scope.location = $location; // 当location.path()的值改变时执行其中的方法 $scope.$watch('location.path()',function (path) { // 获取状态的过滤器 // 如果path为'/active',过滤器为{ completed: false } // 如果path为'/completed',过滤器为{ completed: true } // 否则,过滤器为null $scope.statusFilter = (path === '/active') ? { completed: false } : (path === '/completed') ? { completed: true } : null; }); // 添加一个新的todo $scope.addTodo = function () { var newTodo = $scope.newTodo.trim(); if (!newTodo.length) { return; } // 向todos里添加一个todo,completed属性认为false todos.push({ title: newTodo, completed: false }); // 置空 $scope.newTodo = ''; }; // 编辑一个todo $scope.editTodo = function (todo) { $scope.editedTodo = todo; // Clone the original todo to restore it on demand. // 保存编辑前的todo,为恢复编辑前做准备 $scope.originalTodo = angular.extend({},todo); }; // 编辑todo完成 $scope.doneEditing = function (todo) { // 置空 $scope.editedTodo = null; todo.title = todo.title.trim(); if (!todo.title) { // 如果todo的title为空,则移除该todo $scope.removetodo(todo); } }; // 恢复编辑前的todo $scope.revertEditing = function (todo) { todos[todos.indexOf(todo)] = $scope.originalTodo; $scope.doneEditing($scope.originalTodo); }; // 移除todo $scope.removetodo = function (todo) { todos.splice(todos.indexOf(todo),1); }; // 清除已完成的todos $scope.clearCompletedTodos = function () { $scope.todos = todos = todos.filter(function (val) { return !val.completed; }); }; // 标记所有的todo的状态(true或false) $scope.markAll = function (completed) { todos.forEach(function (todo) { todo.completed = completed; }); }; });

最后看一下index.html,这个文件我们一段一段的分析。

代码如下:
odomvc" data-framework="angularjs"> <Meta charset="utf-8"> <Meta http-equiv="X-UA-Compatible" content="IE=edge"> AngularJS • T<a href="https://www.jb51.cc/tag/odo/" target="_blank" class="keywords">odo</a>MVC odomvc-common/base.css">
odoapp" ng-controller="TodoCtrl">
Box" ng-model="allChecked" ng-click="markAll(allChecked)">
odo-count">

{{remainingCount}}

pluralize count="remainingCount" when="{ one: 'item left',other: 'items left' }">

首先是在最下面,引入相应的JS,这个就不多说了。

代码如下:
odomvc-common/base.js">

定义style[ng-cloak],含有ng-cloak属性则不可见。

代码如下:
[ng-cloak] { display: none; }

来看添加todo的html,绑定的model为newTodo,submit的方法是todoCtrl.js中的addTodo(),会添加一条todo,点击Enter,认触发提交事件,就触发了addTodo()方法添加了一条todo到todos中。

代码如下:
odo-form" ng-submit="addTodo()">

再看展示todos的html

代码如下:
Box" ng-model="allChecked" ng-click="markAll(allChecked)">

section使用ngShow方法根据todos的长度判断是否显示加上ng-cloak属性是为了在刚开始时不要显示出AngularJS未处理的页面。可以去掉刷新试一试。

其中id为toggle-all的checkBox绑定到allChecked model上,点击触发markAll(allChecked),将allChecked的值传入,标记所有的todos。

使用ngRepeat循环产生li标签,todo in todos | filter:statusFilter track by $index,循环todos,用statusFilter过滤,用$index追踪。ngClass绑定了两个class,{completed: todo.completed,editing: todo == editedTodo},如果todo.completed为true,添加completed class,如果todo==editedTodo,则添加editing class。class为toggle的checkBox绑定到todo.completed。todo标题展示的label绑定了双击事件,双击触发editTodo(todo),editTodo会将todo赋给editedTodo,然后会触发下面form中的todoFocus指令,这时候form中的input可见。按Esc就触发revertEditing(todo),恢复到编辑前,按Enter或者失去焦点就触发doneEditing(todo) ,保存编辑后的todo。class为destroy的button绑定了click事件,点击触发removetodo(todo),删除掉该条todo

最后看todos的统计信息展示的html

代码如下:
odo-count">

{{remainingCount}}

pluralize count="remainingCount" when="{ one: 'item left',other: 'items left' }">

ng-pluralize标签实现了当remainingCount个数为1时,显示 item left,否则显示 items left。

id为filters的ul标签中根据location.path()的内容不同,标记不同的a标签被选中。

id为clear-completed的button添加了点击事件,触发clearCompletedTodos(),清除掉所有已完成的todo

今天的笔记就先到这里吧,都是些个人心得,希望小伙伴们能够喜欢。

相关文章

前言 做过web项目开发的人对layer弹层组件肯定不陌生,作为l...
前言 前端表单校验是过滤无效数据、假数据、有毒数据的第一步...
前言 图片上传是web项目常见的需求,我基于之前的博客的代码...
前言 导出Excel文件这个功能,通常都是在后端实现返回前端一...
前言 众所周知,js是单线程的,从上往下,从左往右依次执行,...
前言 项目开发中,我们可能会碰到这样的需求:select标签,禁...