网页版讲义
Folders and files
Name | Name | Last commit date | ||
---|---|---|---|---|
parent directory.. | ||||
<html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>i5ting_ztree_toc:README</title> <link href="toc/style/github-bf51422f4bb36427d391e4b75a1daa083c2d840e.css" media="all" rel="stylesheet" type="text/css"/> <link href="toc/style/github2-d731afd4f624c99a4b19ad69f3083cd6d02b81d5.css" media="all" rel="stylesheet" type="text/css"/> <link href="toc/css/zTreeStyle/zTreeStyle.css" media="all" rel="stylesheet" type="text/css"/> <style> pre { counter-reset: line-numbering; border: solid 1px #d9d9d9; border-radius: 0; background: #fff; padding: 0; line-height: 23px; margin-bottom: 30px; white-space: pre; overflow-x: auto; word-break: inherit; word-wrap: inherit; } pre a::before { content: counter(line-numbering); counter-increment: line-numbering; padding-right: 1em; /* space after numbers */ width: 25px; text-align: right; opacity: 0.7; display: inline-block; color: #aaa; background: #eee; margin-right: 16px; padding: 2px 10px; font-size: 13px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } pre a:first-of-type::before { padding-top: 10px; } pre a:last-of-type::before { padding-bottom: 10px; } pre a:only-of-type::before { padding: 10px; } .highlight { background-color: #ffffcc } /* RIGHT */ </style> </head> <body> <div> <div style='width:25%;'> <ul id="tree" class="ztree" style='width:100%'> </ul> </div> <div id='readme' style='width:70%;margin-left:20%;'> <article class='markdown-body'> <h2 id="-">说明</h2> <ul> <li>这个todoMVC-template 截至2017-7-8,通过<code>npm install</code>下载的css样式还是有问题,建议大家直接用我提供的样式即可</li> <li>遇到bug,多检查一下自己是不是少写或漏写了啥,是不是单词写错了,这种出错误的可能性很高</li> <li>如果有兴趣的同学,可以想办法把这个项目一步一步的写成vue,react.js的版本,甚至jq的版本 ^_^</li> </ul> <h2 id="-">需求分析</h2> <ul> <li>见视频</li> <li>自己用鼠标操作一把</li> </ul> <h2 id="-">功能说明</h2> <ul> <li>和视频中保持一致,没有标准答案</li> </ul> <h2 id="-">任务展示</h2> <ul> <li>分析需求:想办法把ul>li的列表展示出来</li> <li>一开始如果没有数据没办法玩,就和ajax一样,先构造一些假数据,后期再把假数据去掉,换成真的数据</li> <li>根据双向数据绑定的原理,看到列表,我们要能想到的是数组 --> 观察页面上的数据,自己想一下应该如何构造出来这个列表 --> 任务有名字、有是否完成 --> {name:'xxx',completed:true} --> 为了以后便于在数据库中存,我们多加一个属性id(这块想不到没关系,我们真实的数据一般的是后台给我们的) --> 把数据通过ng-repeat展示出来<h3 id="-">关键代码:</h3> </li> </ul> <pre><code class="lang-javascript">$scope.todos = [ {id:1,name:'吃饭',completed:true}, {id:2,name:'睡觉',completed:true}, {id:3,name:'打豆豆',completed:false}, {id:4,name:'学习',completed:true}, {id:5,name:'喝水',completed:false} ]; </code></pre> <p>这块有的同学会有疑问,为什么一定要id,这个从二方面来说明,第一方面,我们其实不加id是可以的,我们可以考虑用$index来替代,另一方面,我们加了id有一个好处,大家如果看过mysql数据库里面的数据就知道,一般一条数据记录都会带有一个id的,这样写是为了后面更容易和后台进行对接</p> <pre><code class="lang-html"><li ng-repeat="item in todos"> <div class="view"> <input class="toggle" type="checkbox"> <label>{{item.name}}</label> <button class="destroy"></button> </div> <input class="edit" value="Rule the web"> </li> </code></pre> <h2 id="-">任务添加</h2> <ul> <li>分析需求:当我们按住回车的时候,会发现数据添加到了列表当中 <ol> <li>如何确定啥时候我们按住了回车 --> 方式一:用事件对象 方式二:用ng-submit(利用了表单提交的特性,见补充代码/代码表单提交特性.html)</li> <li>新的任务添加到列表当中 --> 根据双向数据绑定的原理,本质上其实是添加到数组当中了 --> 我们需要再构造一个新的数组项 --> 这个新的数组项的值好确定,是否完成也好确定(百分百是未完成),id的话如何确定?(随便来一个,就用Math.random()当然不是特别好,有可能会出现重复,但可能性其实也不是很大) --> push进去 </li> </ol> </li> <li>测试代码,发现有二个bug:<ol> <li>bug1:添加新任务的表单应该清空 --> </li> <li>bug2:当新任务的表单控件是空的情况下,仍然可以按回车</li> </ol> </li> </ul> <h3 id="-">关键代码:</h3> <pre><code class="lang-html"><form ng-submit="add()"> <input ng-model="newTodo" class="new-todo" placeholder="What needs to be done?" autofocus> </form> </code></pre> <pre><code class="lang-javascript">$scope.newTodo='' // ng-model $scope.add = function(){ // 判断newTodo是否为空,为空则不添加任务 if(!$scope.newTodo){ return } // 把新任务添加到$scope.todos中去 $scope.todos.push({ id:Math.random(), name:$scope.newTodo, completed:false }) // 置空 $scope.newTodo='' } </code></pre> <h3 id="-">补充</h3> <ul> <li>大家可以考虑使用事件对象($event --> keyCode的值是13的时候意味着你点击的是回车,见补充代码/angular事件对象.html) --> 来实现这个功能</li> </ul> <h2 id="-">删除任务</h2> <ul> <li>分析需求:当我们点击每一项的x的时候,就会把当前的删除掉<ol> <li>根据双向数据绑定的原理,把当前的这一项删掉,意味着数组中少了一项</li> <li>我们点击的是页面中的每一项,如何确保我们要把对应的数组这一项删掉?<ul> <li>思路一:把id传过来,然后遍历数组,看谁的id刚好相等</li> <li>思路二:通过把$index传过来</li> </ul> </li> </ol> </li> </ul> <h3 id="-">关键代码:</h3> <pre><code class="lang-html"><li ng-repeat="item in todos"> <div class="view"> <input class="toggle" type="checkbox"> <label>{{item.name}}</label> <button class="destroy" ng-click="remove(item.id)"></button> </div> <input class="edit" value="Rule the web"> </li> </code></pre> <pre><code class="lang-javascript">//删除任务 $scope.remove = function(id){ // 根据id到数组$scope.todos中查找相应元素,并删除 for (var i = 0; i < $scope.todos.length; i++) { var item = $scope.todos[i] if(item.id === id){ $scope.todos.splice(i,1) // 删除数据 return } } } </code></pre> <h2 id="-">修改任务</h2> <ul> <li>分析需求:双击的时候,当前的任务变成了一个文本框<ol> <li>ng-dblclick</li> <li>这个神奇的效果本质:有二个标签,一个是label,一个是input,如果是平时,label显示,如果是编辑的时候,input显示 --> 对应的editing类名</li> <li>一说到类名,我们会想起ng-class --> {editing:true或者false} --> 在页面上玩一下 --> 当然这个true或者false不能写死</li> <li>我们看一下刚才做的删除任务,利用的是传过去id值 --> 我们修改任务其实也是可以传过去id的 --> 如何才能构造出来一个true或false? --> 传进去的id的值和当前的item.id进行比较 --> 如何确保一开始的时候这个isEditingId的值不和任何任务的id相等 -> 设置成-1</li> <li>如何当我们按回车的时候,变回非编辑状态 --> 根据新添加任务做的,我们用同样的思路(ng-submit) --> 事实上,只要把isEditingId的值变成-1就可以</li> </ol> </li> </ul> <h3 id="-">关键代码:</h3> <pre><code class="lang-html"><li ng-repeat="item in todos" ng-class="{editing:isEditingId == item.id}"> <div class="view"> <input class="toggle" type="checkbox"> <label ng-dblclick="edit(item.id)">{{item.name}}</label> <button class="destroy" ng-click="remove(item.id)"></button> </div> <form ng-submit="save()"> <input class="edit" value="Rule the web" ng-model="item.name"> </form> </li> </code></pre> <pre><code class="lang-javascript">//修改任务 $scope.isEditingId = -1 $scope.edit = function(id){ $scope.isEditingId = id } // 只是改变也文本框的编辑状态 $scope.save = function(){ $scope.isEditingId = -1 } </code></pre> <h2 id="-">切换任务状态</h2> <ul> <li>需求分析:当点击当前任务的时候,当前的任务状态由已完成变成未完成,或者未完成变成已完成<ol> <li>我们点击的是input[type="checkbox"],我们根据前面学习的知识知道,input[type="checkbox"]的值是true或者false</li> <li>我们观察发现右侧的列表的样式在发生变化,其实就是给父级元素添加一个completed的类名 --> 一说到类名,我们可以想到用ng-class</li> <li>刚好input[type="checkbox"]的值也是true和false,所以,我们直接让checkbox和右侧的label对应的类名是一个数据模型</li> </ol> </li> </ul> <h3 id="-">关键代码:</h3> <pre><code class="lang-html"><li ng-repeat="item in todos" ng-class="{editing:isEditingId == item.id,completed:item.completed}"> <div class="view"> <input class="toggle" type="checkbox" ng-model="item.completed"> <label ng-dblclick="edit(item.id)">{{item.name}}</label> <button class="destroy" ng-click="remove(item.id)"></button> </div> <form ng-submit="save()"> <input class="edit" value="Rule the web" ng-model="item.name"> </form> </li> </code></pre> <h2 id="-">批量切换任务状态</h2> <ul> <li>当点击最上面的checkbox的时候,所有的任务批量切换已完成和未完成的状态<ol> <li>checkbox对应的ng-model的值是true或false</li> <li>下面所有的任务只要改变item.completed的值都是true或false则实现效果</li> <li>所以,我们考虑给最上面的checkbox添加一个事件(ng-change,当值变化的时候触发),当触发的时候,我们对数据模型中的数组做一个遍历,让每一项的值都同步为true或false</li> </ol> </li> </ul> <h3 id="-">关键代码</h3> <pre><code class="lang-html"><input class="toggle-all" type="checkbox" ng-change="toggleAll()" ng-model="selectAll"> </code></pre> <pre><code class="lang-javascript">$scope.selectAll = false $scope.toggleAll = function(){ // 让$scope.todos中所有数据的completed值等于$scope.selectAll for (var i = 0; i < $scope.todos.length; i++) { var item = $scope.todos[i] item.completed = $scope.selectAll } } </code></pre> <h2 id="-">显示未完成任务数</h2> <h3 id="-n-">见代码《补充代码/加法计算器实现的N种方法》的分析,理解:如果一个表达式是由多个别的数据模型或者是某个函数(返回值由多个别的数据模型运算结果运算而来)的返回值的话,当这些多个别的数据模型的值发生变化的时候,也会重新刷新一下当前这个表达式的值</h3> <ul> <li>需求分析:当我们切换任务状态的时候,我们发现未完成任务数在跟着发生变化<ol> <li>我们计算出来未完成的任务数其实很简单,我们只需要计算出来数据模型中的数组中未完成任务的数量即可</li> <li>所以,我们考虑在这块放一个函数的返回值</li> </ol> </li> </ul> <h3 id="-">关键代码:</h3> <pre><code class="lang-html"><span class="todo-count"><strong>{{getActive()}}</strong> item left</span> </code></pre> <pre><code class="lang-javascript">$scope.getActive = function(){ var count = 0 // 遍历$scope.todos, 找到所有completed属性值为false的数据 for (var i = 0; i < $scope.todos.length; i++) { var item = $scope.todos[i] if(!item.completed){ count++ } } return count } </code></pre> <h2 id="-">清除所有已完成任务数</h2> <ul> <li>当我们点击clear completed的时候,我们发现,任务列表中已完成的任务会被删除<ol> <li>给clear completed这个按钮添加ng-click</li> <li>当点击触发的时候,我们<h3 id="-">关键代码:</h3> <pre><code class="lang-html"><button class="clear-completed" ng-click="clearAll()">Clear completed</button> </code></pre> </li> </ol> </li> </ul> <pre><code class="lang-javascript">$scope.clearAll = function(){ for (var i = $scope.todos.length - 1; i >= 0; i--) { // true(0),false(1),false(2) var item = $scope.todos[i] if(item.completed){ $scope.todos.splice(i,1) } } } </code></pre> <h2 id="-filter-">使用filter实现切换不同状态任务的显示</h2> <ul> <li>需求分析:当我们点击all,completed,active的时候,我们发现任务列表在进行切换<ol> <li>思考:我们能不能参考clear completed的思路来实现? ---> 不可以,因为我们如果真的在点击active的时候,把completed的全部删除了,那么我们再点击回来的时候,就点击不回来了</li> <li>针对这种情况,我们考虑使用angular的过滤器来实现</li> </ol> </li> </ul> <h2 id="filter-">filter过滤器的使用(只讲数组过滤数据中的模糊匹配和精确匹配)</h2> <p>见《补充代码/filter过滤器的使用》</p> <h3 id="-">关键代码:</h3> <pre><code class="lang-html"><li ng-repeat="item in todos | filter : isCompleted" ng-class="{editing:isEditingId == item.id,completed:item.completed}"> </code></pre> <pre><code class="lang-javascript">//切换不同状态任务的显示 $scope.isCompleted = {};//filter过滤器的过滤条件 //显示未完成的任务 $scope.active = function(){ $scope.isCompleted = {completed:false} } // 显示已完成任务 $scope.completed = function(){ $scope.isCompleted = {completed:true} } //显示所有的任务 $scope.all = function(){ $scope.isCompleted = {} } </code></pre> <h2 id="-">切换不同状态焦点状态样式的操作</h2> <ol> <li>点击显示已完成的任务,当前有selected类名样式,其他二个没有</li> <li>一说到样式,我们会想起来ng-class</li> <li>我们必须在ng-class里面构造出来一个布尔值</li> </ol> <h3 id="-">关键代码:</h3> <pre><code class="lang-html"><ul class="filters"> <li> <a ng-class="{selected:isCompleted.completed==undefined}" href="#/" ng-click="all()">All</a> </li> <li> <a ng-class="{selected:isCompleted.completed==false}" href="#/active" ng-click="active()">Active</a> </li> <li> <a ng-class="{selected:isCompleted.completed==true}" href="#/completed" ng-click="completed()">Completed</a> </li> </ul> </code></pre> <h2 id="-watch-">通过$watch监视锚点值的变化来切换不同状态任务的显示与否</h2> <ul> <li>观察我们做的和官网上的,发现有区别:官网上面当点击all,completed,active的时候,url中的hash锚点值在发生变化,并且当我们把官网上的url拷贝到一个新的标签的时候,按回车,如果hash值是active,则显示的是active状态,所以,我们要改造《使用filter实现切换不同状态任务的显示》这一步中的通过ng-click实现的状态任务的切换</li> <li>思考:我们如何才能根据锚点(hash)值的变化进行不同状态任务的切换呢? --> 回忆一下我们在第一天讲angular.js的时候,用原生的js实现的spa的小demo --> 我们需要监听hash值的变化,当发生变化的时候,我们改变$scope.isCompleted的值,只不过,我们这里不是用的hashchange事件,而是利用的$location(参考代码《补充代码/$location.html》) + angular的监视$watch来实现(参考代码《补充代码/》)</li> </ul> <h3 id="-">关键代码:</h3> <pre><code class="lang-html"><ul class="filters"> <li> <a ng-class="{selected:isCompleted.completed==undefined}" href="#/">All</a> </li> <li> <a ng-class="{selected:isCompleted.completed==false}" href="#/active" >Active</a> </li> <li> <a ng-class="{selected:isCompleted.completed==true}" href="#/completed">Completed</a> </li> </ul> </code></pre> <pre><code class="lang-javascript">myApp.controller('myController',['$scope','$location',function($scope,$location){ </code></pre> <pre><code class="lang-javascript">//切换不同状态任务的显示 $scope.isCompleted = {};//filter过滤器的过滤条件 //显示未完成的任务 // $scope.active = function(){ // $scope.isCompleted = {completed:false} // } // // 显示已完成任务 // $scope.completed = function(){ // $scope.isCompleted = {completed:true} // } // //显示所有的任务 // $scope.all = function(){ // $scope.isCompleted = {} // } $scope.loca = $location $scope.$watch('loca.url()',function(now, old){ switch(now){ case '/active': $scope.isCompleted = {completed:false} break; case '/completed': $scope.isCompleted = {completed:true} break; default: $scope.isCompleted = {} } }) </code></pre> <h2 id="-service">提取控制器的代码到service</h2> <p>见《补充代码/理解service》</p> <h2 id="-">抽取获取数据及添加数据功能</h2> <ul> <li>注意:我们当前的代码写到这个程度其实已经可以了,下面的所谓的提取到service只不过是为了让我们熟悉和了解service这个概念</li> <li>提取到服务的原则<ul> <li>哪些东西可以被复用?</li> <li>当前的项目中涉及到的数据来自哪里? --> localStorage --> 对localStorage的在任何控制器里面都可以使用 --> 把对localStorage中的数据的各种操作提取出来</li> </ul> </li> </ul> <h3 id="-">关键代码</h3> <pre><code class="lang-html"><script src="./libs/angular.js"></script> <script src="./js/service.js"></script> <script src="js/app.js"></script> </code></pre> <pre><code class="lang-javascript">(function(angular){ // 1.创建服务模块 var app = angular.module('service',[]) // 2.创建服务 // 第一个参数:服务的名字 // 第二个参数里的function: // angular会把这个function当作构建函数,angular会帮助我们new这个构建函数,然后得到一个对象。A,B app.service('MyService', ['$window',function($window){ // console.log($window === window); var str = $window.localStorage.getItem('mytodos')|| '[]' var todos = JSON.parse(str); // 1.返回任务数据 this.get = function(){ return todos } // 2.添加数据 this.add = function(newTodo){ // 把新任务添加到todos中去 todos.push({ id:Math.random(), name:newTodo, completed:false }) var str = JSON.stringify(todos); $window.localStorage.setItem('mytodos',str); } }]) })(angular) </code></pre> <pre><code class="lang-javascript">ar myApp = angular.module('myApp',['service']); myApp.controller('myController',['$scope','$location','MyService',function($scope,$location,MyService){ //要展示的任务数据 $scope.todos = MyService.get(); //添加任务 $scope.newTodo='' // ng-model $scope.add = function(){ // 判断newTodo是否为空,为空则不添加任务 if(!$scope.newTodo){ return } // 把新任务添加到$scope.todos中去 MyService.add($scope.newTodo); // 置空 $scope.newTodo='' } </code></pre> <h2 id="-">抽取删除数据及批量切换任务状态功能</h2> </article> </div> </div> </body> </html> <script type="text/javascript" src="toc/js/jquery-1.4.4.min.js"></script> <script type="text/javascript" src="toc/js/jquery.ztree.all-3.5.min.js"></script> <script type="text/javascript" src="toc/js/ztree_toc.js"></script> <script type="text/javascript" src="toc_conf.js"></script> <SCRIPT type="text/javascript" > <!-- $(document).ready(function(){ var css_conf = eval(markdown_panel_style); $('#readme').css(css_conf) var conf = eval(jquery_ztree_toc_opts); $('#tree').ztree_toc(conf); }); //--> </SCRIPT>