欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

宁皓网 Backbone 应用实例 有大用

这个应用实例课程里,算是对我们所学的 JavaScript 的一个期中总结。用到了 Backbone,Underscore,jQuery,还有 JavaScript 的基础。


来自  https://ninghao.net/course/1510#info

1)预览

我们在这个课程里要做的一个实例是一个任务列表的小应用,在应用的主页面上,会有一个大的文本框 ... 在这里,可以输入要做的事情 ....

晚上回家洗尿布 ...

按一下回车键 .. 应用会根据在文本框里输入的东西,创建一个新的任务 .. 显示在下面 ... 再去创建几条任务 ...

已经完成的任务,可以点击任务项目左边的这个对号,这其实就是一个添加了额外样式的复选框 ...

在页脚这个区域上面,会显示一些状态信息 ... 比如没有完成的任务数量 ... 还有已经完成的任务 ... 点击这个 清除已完成,可以删除掉完成的任务 ....

这中间还有一个简单的导航,用的是 Backbone 的路由器 ... 点击 进行中 ... 会显示没有完成的任务 ..

点击 已完成,会从任务列表里面,过滤出已经完成了的任务 ..

全部,会显示全部的任务 ...

想要编辑任务项目,可以双击它(#双击 录制视频) ... 这样会出现一个文本框 ,里面已经填好了当前任务的内容 ... 然后可以去修改一下 .. (#添加 Backbone),完成以后,再按一下回车,可以保存修改的内容 ...

鼠标放到任务项目的上面 ... 在它右边儿会显示一个 叉号,点击这个叉号, 可以删除掉这个任务 ....

在新建任务的这个文本框的左边,还有一个复选框 ... 勾选一下 ... 可以把所有的任务都标记成已完成的状态 ...

再点一下右下角的 清除已完成 ... 可以把所有的已经完成的任务都删除掉。

来自  https://ninghao.net/video/1511#toc

准备

2)准备

下面,我们去准备一下这个应用程序需要用到的一些文件还有组织文件的目录结构。

可以先找一个地方,新建一个目录 ... 比如在桌面上 .. 新建目录 ... 命名为 todos .. 在这个目录下面,可以存储应用程序需要的所有的东西。

再打开编辑器 ... 这里我用的是 Brackets 编辑器 ... 打开 文件 菜单,选择 打开目录 ... 找到刚才创建的目录 ...

在这个编辑器的边栏上,会显示出这个目录里面的所有的东西。 在这里,我们也可以直接在这个 todos 目录下面,去新建文件或者目录 ..

先创建一个 js 目录 ... 然后在这个目录里面,再去添加几个目录,在这些目录里分别存放应用程序不同部分的代码。

先创建一个 Models 目录 ... 这里放的是应用需要的模型 ... 在这里再去创建一个文件 ... 命名为 Todo.js ... 因为我们要创建的是一个简单的任务管理应用,Todo 这个词的意思就是任务 ... 一会在这个文件里面,去创建应用需要的模型。

然后再创建一个目录 ... 叫做 Collections ... 这里放的是应用需要的集合 .. 在这个目录下面,添加一个文件 .. 命名为 TodoCollection.js ... Todo 表示任务,Collection 是集合的意思,这个集合表示的就是任务的列表。

再添加一个存放应用视图文件的目录 ... 叫做 Views ... 然后在这里面,去创建两个视图文件... 一个叫做 TodoView.js ... 这里面是任务的视图 ... 再添加一个 AppView.js ... 它表示的是整个应用的视图 ...

最后再添加一个 Routers 目录 ... 里面新建一个文件 .. 命名为 Router.js ... 这里放的是应用的路由器 ...

然后我们可以直接在 js 这个目录的根下面,再添加一个 app.js .. 在这个文件里面,可以放一些启动应用需要的一点代码 ...

lib

在应用的根目录下面,再创建一个叫 lib 的目录 .. 它表示 library ... 我们可以把应用需要的一些类库放到这里面 ... 比如 backbone ,underscore ,jQuery 等等 ..

你可以去把这些类库下载下来,放到这里面 ... 这里我已经事先下载好了 ...

找到这几个类库,把它们复制到应用的 lib 目录的下面 ...

assets

然后,我们再把应用需要的图片还有样式表这些资源,放到 assets 这个目录的下面 ... 在这个课程的资源包里,你可以找到这些东西 ...

复制一份,放到应用的根目录下面 ... 这个 assets 里面,有两个文件 ... 一个是背景图片 ... 还有一个是应用需要的一些 css 样式 ...

index.html

回到编辑器 ... 在应用的根目录下面,我们再去新建一个 html 文档,这个文档就是应用的主页面 ... 它里面会包含应用需要所有的东西 ... 类库,代码,样式表,还有模板, html 结构等等。

先创建一个简单的 html 文档的结构 ... 如果你在 Brackets 编辑器里安装了 emmet 插件,可以这样 ... 输入一个叹号 ... 现按一下 tab 键 ...

修改一下 lang 属性 ... 再改一下 title 属性的值 ...

然后,在 head 标签里面,去把应用需要的样式表链接过来 ... 这个样式表是在 assets 目录下面 ... 叫做 base.css ...

然后在 body 结束的标签的上面,去把应用需要的类库,还有代码文件都嵌入到这个文档里面 ...

首先要嵌入进来的是 jQuery ...

然后是 underscore 这个类库 ... 接着是 backbone .....

还有一个 Backbone 的 localStorage 插件。这个东西的作用就是,覆盖了 Backbone 默认的 Sync 方法 ... 默认 Backbone 是会向指定的地址发出请求。这个插件会让 Backbone 的请求都指向浏览器的 LocalStorage 上面 ... 也就是,Backbone 会从 LocalStorage 里面读取数据,或者存放数据等等 ...

<!-- 依赖的各种类库 -->
<script src="lib/js/jquery-2.1.1.min.js"></script>
<script src="lib/js/underscore-min.js"></script>
<script src="lib/js/backbone-min.js"></script>
<script src="lib/js/backbone.localStorage.js"></script>

最后,我们再把应用需要的一些代码文件也嵌入到这个文档里面 ...

<!-- 应用程序代码 -->
<script src="js/Models/Todo.js"></script>
<script src="js/Collections/TodoCollection.js"></script>
<script src="js/Views/TodoView.js"></script>
<script src="js/Views/AppView.js"></script>
<script src="js/Routers/Router.js"></script>
<script src="js/app.js"></script>

现在,我们就准备好了应用程序需要的基本的结构 ...

来自  https://ninghao.net/video/1512#info


3)HTML


下面,我们再去准备一下应用程序需要用到的 HTML 的代码。

在文档的 body 标签的里面 ... 先添加一组 div 标签,上面再加上一个叫 todoapp 的 id ..

<div id="todoapp"></div>

header

然后在组标签里面,再添加一组 header 标签 ... 这个标签的上面添加一个 header id ... 这里面的东西是应用的头部 ...

<header id="header"></header>

加上一组 h1 标题标签 ... 里面的内容是 todos ...

<h1>todos</h1>

然后,再加上一个 text 类型的 input 元素,也就是一个文本框 ... 在这个文本框里,输入内容,然后按一下回车 ... 会在应用里面添加一条新的任务 ...

在这个标签上,加上一个 new-todo 的 id ... 再添加一个点位符的属性 ... placeholder ... 它的值是 需要做点什么呢?

最后再添加一个 autofocus ,让这个文本框自动处在焦点状态 ....

<input type="text" id="new-todo" placeholder="需要做点什么呢?" autofocus>

main

下面再添加应用的主体部分 ... 用一组 section 标签 ... 上面加上一个叫做 main 的 id ...

先在这里添加一个复选框 ... 它的作用就是,可以切换所有的任务的状态 ... 勾选它的话会把所有的任务都设置成完成的状态 .. 取消勾选,会把所有的任务都设置成进行中的状态 ..

在这个元素上面,再加上一个 id ,可以叫做 toggle-all ... 元素的名字也可以叫做 toggle-all

<input type="checkbox" name="toggle-all" id="toggle-all">

我们可以再给这个元素添加一个标签 ...

<label for="toggle-all">标记全部为完成</label>

然后在主体部分里面,主要的内容是任务的列表 ... 这里我们添加一组 ul 标签 ... 上面加上一个叫 todo-list 的 id ..

<ul id="todo-list"></ul>

在后面,我们会通过代码,把渲染之后的任务模型的列表都到这个无序列表元素里面。

footer

在应用主体结构的下面,再添加一个页脚 .... 可以使用 footer 标签 ... 上面同样需要一个 id ... 可以做它 footer

<footer id="footer"></footer>

在后面我们会把一些状态信息放到这里面 .. 比如还剩下多少个任务 ... 已经完成的任务有多少,另外还有一个简单的导航,可以切换显示全部的任务,完成的任务,还有进行中的任务 ...

info

最后在这个 todoapp 标签的外面 .. . 再添加一组显示提示信息的标签 ... 上面加上一个叫 info 的 id ...

<div id="info">
<p>双击可以编辑任务</p>
</div>

现在, 我们就准备好了,应用需要的基本的 HTML 结构 ...

来自  https://ninghao.net/video/1513


创建任务

4)创建新任务的功能 - 创建任务模型

我们先完成应用里面的一个小功能,就是创建新的任务,在这个文本框里输入内容,按下回车,新的任务会显示在下面。

我们要做的是一个管理任务列表的小应用,在这个应用里,每个任务项目是应用要处理的数据。所以,我们需要先去创建一个模型,用来表示任务项目。

应用的模型,可以放在 Models 这个目录的下面,我们之前已经创建了一个 Todo.js ,在这个文件里,可以去创建应用需要的模型。

先打开这个文件。

命名空间

在这个应用里面,可以先定义一个对象,然后把应用需要的东西都作为这个对象里的属性,这个对象的作用有点像是一个命名空间,也就是我们在全局范围内,只定义了一个东西,就是这个对象。这样不容易跟第三方的代码库发生命名的冲突。

这个对象可以叫做 app ,表示 application ... 你也可以根据自己的需求去命名一下它 ...

var app = app || {};

这行代码的意思就是,去定义一个叫 app 的对象,它的值,要么等于 app ,要么就是一个空白的对象。 一组花括号,表示一个空白的对象。

模型

接下来, 我们为应用创建的模型,集合之类的东西,都可以作为 app 这个对象的属性添加进来。下面,我们先去创建在应用里表示任务这个类型的数据的模型。这个模型可以叫做 Todo ...

app.Todo ... 意思就是给 app 这个对象添加一个 Todo 的属性 ... 创建 Backbone 的模型 ... 可以让它等于 Backbone 的 Model 的 extend ...

创建 Backbone 的模型,可以参考宁皓网的 Backbone 基础教程。

在这个模型里面,添加两个默认的属性 ... 这些默认的属性放在 defaults 这个属性里面。

defaults: {}

先添加一个 title 属性 ... 这里面放的是任务的标题内容 ... 默认让它等于空白的字符串 ...

然后再添加一个 completed 属性 ... 这个属性表示任务的状态,如果它的值是 true ,就表示任务已经完成了,如果它的值是 false ,表示任务还在进行中 ... 默认我们让它的值默认等于 false ...

defaults: {
title : '',
completed : false
}

在后面,我们还会回来继续去给这个模型里添加其它的东西 ... 暂时先这样 .. 保存一下 ...

来自  https://ninghao.net/video/1514#info

5)创建任务集合(任务列表)


下面,我们再去创建一个表示任务列表的集合,在 Backbone 里面,数据的列表就是用集合来表示的,英文是 Collection ... 应用的集合,可以放在 js 这个目录下面的 Collections 这个目录的里面。

在这个目录里面,之前我们已经创建好了一个叫 TodoCollection.js 的文件 ... 打开这个文件 ... 然后把集合的代码放到这个文件里面。

在文件的顶部,同样需要一个表示命名空间的对象 ...

var app = app || {};

然后把要创建的集合作为这个对象的一个属性 ...

app.TodoCollection ... TodoCollection 就是要创建的这个集合的名字 ... 创建一个 Backbone 的集合,可以让它等于 Backbone 的 Collection 的 extend ..

app.TodoCollection = Backbone.Collection.extend({});

在这个集合里面,先去指定一下跟这个集合相关的模型是哪个 ... 添加一个 model 属性 ... 让它的值等于 app.Todo ...

model: app.Todo;

这个 app.Todo 就是在上一个视频我们创建的用来表示任务的模型。这样,在这个集合里面的模型,都会认为是 Todo 这个类型的模型。

localStorage

在我们的这个小应用里面,用到了 Backbone 的 localStorage 插件 ... 也就是所有要处理的数据,都会存储到浏览器的 localStorage 里面。

想要使用它,我们需要在集合里面,去新建一个 localStorage .. 在这里,添加一个 localStorage 属性 ... 等于 new Backbone.LocalStorage('todo') .. 这里的 todo ,有点像是一个存储的数据的前缀 ... 也可以把它想像成是一个命名空间,也就是为了避免冲突,起的一个名字 ...

localStorage: new Backbone.LocalStorage('todo'),

新建集合实例

在这个文件的下面,去实例化一下这个集合 ... 也就是根据这个集合去创建一个集合的对象 ...

app.todoList = new app.TodoCollection;

app.todoList 就是我们创建的一个集合对象 ... 在应用里面,可以用到它 ...

这里创建的这个集合,在后面我们还会再回来继续去修改它 ...

来自  https://ninghao.net/video/1515

6)理解 Backbone 的 localStorage

下面,我们到浏览器上去试一下 ... 在浏览器里边儿直接打开应用的主页 ... index.html ...

然后打开浏览器的控制台 ... alt + command + J

在控制台上,先查看一下 app.todoList 这个集合里面的东西 ...

app.todoList

在返回的结果里面,你会发现, length 属性的值是 0 ,这就表示在这个集合里面,还没有任何的模型 ... length 这个属性通常会表示长度,或者数量 ...

下面,我们用 Backbone 集合里面的 create 这个方法,去创建一个模型 ... 这个方法可以把创建的模型直接添加到集合里面,并且 会向指定的服务端的地址发出请求来存储创建的模型。不过在我们的应用的集合里面,用了 Backbone 的 localStorage ,所以,会把模型存储到浏览器的 localStorage 里面。

app.todoList.create({title: '发布新的课程'})

再查看一下这个集合 ...

app.todoList

这回你会发现,表示任务列表的这个集合的 length 属性的值,现在是 1,说明它里面已经有了一个模型了 ... 也就是刚才我们用 create 方法创建的那个模型 ...

然后再打开 Resources ... 找到 Local Storage ... 打开存储在 file:// 这个路径下面的 Local Storage ... 因为这里我们是直接在文件系统里打开的这个应用的主页 ...

在这里,你会发现,刚才创建的那个任务模型 ...

这条数据的 Key 是用 todo 开头的 ... 后面是一串随机的字符串 ...

数据的 Value ,也就是数据的值 ... 是一个 JSON 类型的数据 ... 在这里,你会发现它的 title 属性的值是,发布新的课程 ...

任务的 completed 这个属性的值是默认的 false ... 另外,Backbone 的 localStorage 这个插件会在存储的数据里面,自动加上一个 id 属性 ...

再回到 console ...

我们可以刷新一下页面 ... command + R ... windows 上应该是 ctrl + R ...

然后再查看一下 app.todoList ... 现在它里面已经没有东西了 ...

下面, 我们可以使用集合的 fetch 这个方法,从数据源哪里提取出模型数据 ... 默认是从指定的地址里面去提取数据 ... 因为这里我们用到了 Backbone 的 localStorage ... 所以会从浏览器的 Local Storage 里面提取数据 ...

app.todoList.fetch()

再查看一下 ...

app.todoList

这次集合里又有数据了 ... 是从 Local Storage 里面提供出来的 ...

用 at 方法,可以查看在集合里面指定索引号的模型 .. . 0 表示第一个模型 ... 后面再加上一个 attributes 属性 .. 可以得到这个模型的属性 ...

app.todoList.at(0).attributes

调用模型的 destroy 方法,可以销毁这个模型 ...

app.todoList.at(0).destroy()

再查看一下集合 ...

app.todoList

length 属性的值是 0 ,表示集合里已经没有模型了 ...

再打开 Resources 选项卡 ... Local Storage

在这里,之前存放的那个模型,也已经被删除掉了。

来自  https://ninghao.net/video/1516

7)任务的视图(模型视图)


下面,我们可以去给应用里面的单个的任务项目,去添加一个视图。在这个任务模型的视图上面,我们需要去设计任务项目的显示的模板,去监听一些 DOM 还有模型的事件,定义处理的方法等等。在后面,我们会一步一步的去完善这个视图里的东西。

应用的视图,放在了 js 目录下面的 Views 这个目录的下面 ... 打开这里的 TodoView.js 这个文件 ... 我们可以把任务的视图放在这个文件里面。

在文档头部同样需要去使用一个命名空间的对象 ...

var app = app || {};

另起一行,再去定义视图 ... 这个视图可以叫做 TodoView ... app.TodoView ... 让它等于 Backbone 的 View 的 extend ...

然后在这个视图里面,我们先去指定一下每个任务项目的标签 ... 设置一下 tagName 这个属性 ... 它的值就是标签的名字 ... 在这里,我们设置成 li ... 这样每个任务项目都会用一组 li 标签包装一下。

app.TodoView = Backbone.View.extend({
tagName: 'li'
})

模板

下面,我们再去设计一下要用到的模板,这个模板就是组织每个任务项目用的 ... 先来看一下完成之后的应用 ...

这里,每个任务项目,最左边是一个复选框 ... 用来切换任务的状态 ... 接着是任务的标题 ... 最后还有一个 删除 按钮 ... 另外还有一个东西,默认是看不到的 ... 双击这个任务标题 .. .会显示一个文本框 ... 可以用来编辑任务的标题 ...

这些东西需要的元素,还有结构,都可以定义在模板里面。

回到编辑器 ... 打开应用的主页 ... index.html

在这里,我们可以去定义需要的模板 ... 随便找一下地方 ..

Backbone 里面的模板功能是第三方来提供的 ... 这里我们用的是 Underscore 这个类库提供的一个简单的模板功能。

先添加一组 script 标签 ... 加上一个 type 属性 ... 属性的值设置成 text/template ... 这样浏览器不会把它解释成脚本的代码 ...

然后再添加一个 id 属性 ... 这个属性的值,就相当于是模板的名字 .. 一会儿我们需要用到它来定位这个模板里的内容 ...

这里可以叫它 item-template ...

在这组标签里面就是模板的具体的结构 ... 先添加一组 div 标签 ... 上面加上一个叫 view 的 CSS 类 ...

然后在它里面,再添加一个用来切换任务状态的复选框元素 ... 就是一个 checkbox 类型的 input 元素 ... 上面再添加一个 css 类 .. 叫做 toggle ... 在视图里面,我们也需要用到这个类来定义这个切换状态的复选框元素 ...

然后,这个复选框是否是被勾选的 ... 我们需要判断一下 ... 要根据任务的 completed 的值来决定 ... 如果它的值是 true ... 就 添加一个 checked ,表示这个复选框元素是被勾选上的 ... 如果 completed 的值是 false .... 那么就不需要添加这个 checked 属性 ...

可以这样 ....

<input class="toggle" type="checkbox" <%= completed ? 'checked' : '' %>>

这里的 <%= 还有 %> 是 Underscore 的模板功能需要用到的一个特殊的标记 ... 在这组特殊标记里面,我们判断了任务的 completed 属性的值 ... 根据它的值,决定是否要添加 checked 这个属性 ...

另起一行,再添加一组 label 标签 ... 它里面的值,就是任务的标题内容 ...

<label><%= title %></label>

这个特殊标记的值,我们会在视图里面,传递给模板 ...

另外,这个模板里面,还需要一些其它的东西,比如删除任务的按钮,还有编辑任务的文本框 ... 在后面介绍到相关功能的时候,我们再回来修改一下这个模板 ...

保存 ...

来自  https://ninghao.net/video/1517


8)任务项目的模板


在上一个视频里,我们定义了一个模板,下面,我们在视图里面,去利用一下这个模板 .. 打开 TodoView 这个视图..

在个视图里面,可以添加一个模板的属性 ... 这里我们叫它 item-template ... 你可以根据自己的需求去命名一下这个模板属性 ...

它的值是 Underscore 的 template 这个方法 ... 在这个方法里,要把模板的具体的内容交给它 ... 得到在 index.html 里面定义的模板的内容,可以使用 jQuery ... 要定位的元素就是定义模板的 script 标签 ... 这个标签上定义了一个叫 item-template 的 id ... 可以用它来定义这个元素 ... 再调用 jQuery 的 html () 这个方法,得到这个元素里面的具体的内容。

下面, 我们再定义一下视图里的 render 方法 ... 去用这个模板,组织一下任务模型的内容 ... 这样为视图指定了任务模型以后,调用视图的这个 render 方法,渲染一下,视图里面的 el 属性,就会用定义好的模板,还有指定的标签,去组织好任务模型的内容。

render: function() {}

在这个方法里,设置一下视图的 el 属性里面的内容 ... this.$el.html() ,this 表示当前的这个视图对象 ... $el 是把视图的 el 属性转换成一个 jQuery 对象的一个简单的写法 ... jQuery 的 html 这个方法可以设置一下它里面的具体的内容 ..

它的内容就是用这个视图里的 itemTemplate 这个方法 ... 这个方法就是刚才在上面定义的这个 ... 然后把模型里的属性交给这个方法 ... 用 this.model.attributes 可以得到模型的属性 ... 得到的应该是一个对象 ... 在我们定义的模板里面,用代号表示的名字,比如 title ,completed ... 这些东西是跟这个模型里面的属性是对应的 ...

这样在调用这个 render 方法以后,就会用模板里定义的结构,模板里的代号会用属性的具体的值代替 ...

this.$el.html(this.itemTemplate(this.model.attributes));

最后,为了可以使用链式调用 ... 需要再使用一个 return this ... 返回当前的视图 ....

return this;

测试

保存 ... 然后再到浏览器上测试一下 ...

我们可以先新建一个任务模型 .. 直接设置一下这个模型的 title 属性 ...

var todo = new app.Todo({title: '发布新的课程'})

然后再去创建一个任务视图 ... 可以叫它 todoView .... 在创建这个视图的时候,可以指定一下相关的模型 ... 这里我们设置成在上面创建的这个模型 .. todo ...

var todoView = new app.TodoView({model: todo})

再去调用这个视图的 render 方法 ... 这样会用模板去组织一下模型里的数据 ... 因为在这个 render 方法里,返回了视图 ... 所以,可以继续调用其它的方法和属性 ... 比如我们可以得到渲染之后的视图的 el 属性 ...

todoView.render().el

这里你会看到,会用一组 li 标签 ... 这是在定义视图的时候,指定要使用的标签 ... 它里面包装的东西,就是用我们定义好的模板组织好的任务模型的内容 ...

先是一组带 view 类的 div 标签 . 在它里面先是一个用来切换任务状态的复选框 ... 接着是一个任务的标题 ...

最后,使用 jQuery 的方法,可以把这些内容添加到页面上显示出来 ... 在 index.html 里面,显示任务列表的位置上,有一个 ul 元素,上面有一个 todo-list 的 id ,可以使用这个 id 来定义到这个元素 ...

然后再调用 append 方法,把指定的东西,添加到这个元素里面 ... 要添加的东西,就是渲染之后的模型视图 ..

$('#todo-list').append(todoView.render().el)

回车 ... 在页面上,会显示出这个任务项目 ...

来自  https://ninghao.net/video/1518#info


9)应用的视图

下面,我们需要再给应用去创建一个主要的视图 ... 在这个视图里面,处理了应用需要的大部分的逻辑。在这里,也需要去监听一些事件,去定义一些方法,比如处理创建新任务的方法,把任务项目添加到页面上的方法等等。

先去定义一下这个视图。

打开 js 目录下面的 Views ... 在这里找到 AppView.js ,在这个文件里,可以存放应用的主视图 ..

先在这里定义一个 app 对象 ...

var app = app || {};

然后去定义一个视图 .. 这个视图可以叫做 AppView ... 让它等于 Backbone 的 View 的 extend ..

app.AppView = Backbone.View.extend({})

在 Backbone 的视图里面,我们可以手工去创建 el 属性 ... 也就是包装内容的元素 ... 或者,也可以使用页面上现成的东西作为视图的 el 属性 ...

在这个视图里,添加一个 el 属性 ... 它的值,可以是页面上的一个元素 ... 打开 index.html ...

这里的 todoapp 这个 id 的元素,可以作为应用视图的 el 元素 ...

回过来 ... 把 el 元素的值,设置成 #todoapp ,意思就是,使用页面上的 id 是 todoapp 的元素,作为这个视图的 el 元素 ..

启动

下面去创建一个这个视图的实例 ... 我们可以把它放在 app.js 这个文件里面 ... 打开 js 根目录下面的 app.js ... 然后用 jQuery 的 ready 方法 ...

这个方法会在页面加载以后立即执行 ... . 使用的形式就是,把一个函数作为 jQuery 的一个参数 ... 然后在这个函数里面,去新建一个应用视图 ...

$(function() {
app.appView = new app.AppView;
});

测试

保存一下 ... 然后我们再到浏览器上试试 ... 刷新 ... 在控制台上 ... 可以查看一下应用视图的 el 属性 ...

app.appView.el

你会看到,返回来的东西就是在主页上的 todoapp 这个元素 ... 还有它包含的东西 ...

来自  https://ninghao.net/video/1519#info

10)回车以后创建新任务 - keypress 事件

下面,我们要做的事就是,在文本框里,输入任务的内容 .. 按一下回车以后,会去创建一个新的任务模型,并且会把这个任务添加到浏览器的 Local Storage 里面存储 ... 同时会把任务显示到应用的页面上。

实现这个功能,可以这样。我们可以去监听一下用户在这个文本框里按下键盘的事件 ... 也就是,然后去判断一下,按的这个按键是不是回车键 ... 如果是的话,就会根据用户在文本框里输入的内容,去创建一个新的任务,存储这个任务,同时也会把它添加到任务的集合里面。

往集合里添加新的任务,会发生一个添加事件,然后我们需要再去监听这个集合的添加事件,这个事件发生的时候,就去把这个模型追加到应用的界面上显示出来。

视图

先打开应用的视图,然后在这个视图里面,可以添加一个 events 属性 ... 这个属性是一个对象 ... 在这个对象里,可以指定一下要监听的 DOM 事件 ... 还有处理事件要用到的方法 ...

events: {}

在这里,我们要监听的 DOM 事件是按下按键的事件 ... 这个事件的名字叫做 keypress .... 然后再输入一个空格 ... 再去指定一下,在哪个元素上发生的这个事件 .. 这里就是在 #new-todo 这个 id 的元素上发生的按下按键的事件 ... 也就是输入任务内容用的那个文本框 ...

注意这个元素需要包含在视图的 el 属性里面才行。在这个元素上发生按键事件以后,去执行一下 createOnEnter 这个方法 ...

events: {
'keypress #new-todo': 'createOnEnter'
},

在这个视图里面,我们需要再去定义一下这个方法 ...

createOnEnter: function(event) {}

这个方法可以接受一个事件参数 ... 然后,在这个方法里面,我们先去测试一下,怎么样得到用户按下的键盘上的按键 ...

这个按键是在事件对象里面的 which 这个属性里面 ... 我们可以把这个属性输出到控制台上 ...

createOnEnter: function(event) {
console.log(event.which);
}

测试

保存 .. 再到浏览器上去试试 ...

刷新一下 ... 打开控制台 ... 然后在这个文本框里输入一点东西 ... 输入一个字母 n ... 注意这会触发 keypress 这个 DOM 事件 ... 因为在这个元素上监听了这个事件 ... 处理它的方法会在控制台上,输出按下的按键的代号 ...

你会看到,按键 n 的代号是 110 .. 再输入几个字符 ... 在控制台上会显示出对应的代号 ...

下面,我们可以看一下回车键的代号 ... 按一下回车 ...

在控制台上,会显示 13 ... 也就是回车键的代码就是数字 13 ....

在下面的视频里,我们再去利用一下这个按键 ...

来自  https://ninghao.net/video/1520#info


11)如果按的是回车键就去创建新任务

现在,我们已经知道了,回车键的代号是数字 13 ... 找到应用视图里面,处理按键事件的 createOnEnter 这个方法 ... 在这里,用一个 if 语句去判断一下 ...

判断的条件就是,按下的按键如果不是 13 ... 也就是,不是回车键的话 .... 要做的事就是 return; 返回 ... 表示什么也不做 ...

if (event.which !== 13) {
return;
}

在这个 if 语句的下面 ... 再设置一下按的按键如果是回车键的话,要做的事儿 ...

要做的事就是使用任务集合的 create 方法,去创建并存储这个任务 ... 给这个方法一个对象参数 ... 再设置一下任务模型的属性 ...

先设置一下 title 属性 ... 任务的标题 ... 它的值就是用户在文本框里输入的东西 ... 得到这个文本框的值,可以这样 .. 找到当前视图里面的 #new-todo 这个元素 ... 就是输入任务内容的文本框元素 ... 调用 val() 方法 ... 这个方法可以得到文本框里的值 ...

最后再使用一个 trim 方法 ... 这个方法可以去掉前后没用的空格 ..

再设置一下 completed 属性的值 ... 默认这个属性的值是 false ...

app.todoList.create({
title : this.$('#new-todo').val().trim(),
completed : false
});

再另起一行,创建成功以后,可以把文本框里的值设置成空白的 ... 使用当前视图里的 #new-todo 这个元素的 val 方法 ... 这是一个 jQuery 的方法 ... 它可以得到元素里的值,也可以设置元素里面的值 ... 这里我们把元素里的值设置成空白的 ...

this.$('#new-todo').val('');

另外,在这个判断的条件里面,我们再加上一个条件 .. 如果用户输入的东西是空白的 .. 同样要 return ... 不然会存储空白的内容 ...

在这个 if 语句里面 ... 用两个 | 符号 ... 表示 或者 ... 条件是文本框里的值是空白的 ...

this.$('#new-todo').val().trim() === ''

这样的话,有两种情况不会去创建新任务,用户没有按下回车键的时候,还有就是用户输入的东西在清理以后是空白的时候。

测试

保存 ... 下面去浏览器上试试 ..

刷新 ... 在文本框里输入任务的标题 . ... '为宁皓网升级' 按下回车键 ..

打开 Resources 选项卡 ... Local Storage ... 在这里, 可以找到创建的模型 ... 再输入一条新的任务 ... '晚上回家洗尿布' 回车 ..

再刷新一下 ... 同样会显示出新创建的任务数据 ...

来自  https://ninghao.net/video/1521#info

12)把任务项目显示在应用的界面上 - add 事件


下面,我们想办法去把任务项目,或者叫任务的模型,显示到应用的界面上。

往任务的集合里面添加新的任务模型的时候,会触发 add 事件,我们可以去监听这个事件 ... 当发生这个事件的时候,就去把新添加到集合里面的任务项目添加到应用的界面上。

可以在应用视图的初始化的方法里面去监听这个集合的 add 事件 ..

打开这个应用视图的文件 ... 然后添加一个初始化的方法 ... initialize ..

initialize: function() {}

然后在这个方法里面,可以使用 Backbone 的 listenTo 这个方法,去监听事件 ... this.listenTo ,表示,使用这个视图的 listenTo 方法 ... 然后在这个方法里面,先指定一下发生这个要监听的事件的对象 ... 这里就是应用的集合对象 ... 可以使用 app.todoList 来表示 ...

第二个参数是要监听的事件 ... 这里就是 add 这个事件 ... 最后,再指定一下处理这个事件要用到的方法 ... this.addOne ... 表示使用这个视图里面的 addOne 这个方法去处理发生在任务集合上面的 add 这个事件 ...

this.listenTo(app.todoList, 'add', this.addOne);

在下面,我们再去定义这个 addOne 方法 ... 发生 add 事件的时候,会把模型传递给处理它的方法 ... 这里,我们用 todo 来表示这个传递过来的模型 ...

addOne: function(todo) {}

然后在这个方法里面,可以根据这个任务项目去创建一个新的任务视图 ... 然后再把渲染之后的任务视图的 el 属性里面的东西添加到界面上 ...

var todoView = new app.TodoView({model: todo});

这个视图叫做 todoView ... 在新建这个视图的时候,设置一下 model 属性 ... 对应的值,就是传递过来的那个任务模型 ,这里我们用 todo 来表示这个模型数据。

然后用 jQuery ,找到 #todo-list 这个元素 ... 打开 index.html ... 在这里,你可以看到一个 ul 元素,上面有一个 id 就是 todo-list , 在这个元素里面的东西就是任务项目的列表。

回到视图 ... 找到以后,用 jQuery 的 append 方法,去把渲染以后的任务视图的 el 属性,追加到这个元素上去 ...

$('#todo-list').append(todoView.render().el);

测试

先保存一下 ... 下面,还是先到浏览器上试试 ...

在文本框里输入任务的标题 ... 给小孩买奶粉 ... 按下回车 ...

你会看到,在下面, 直接会显示出新创建的这个任务 ...

下面, 刷新一下这个应用的主页 ... 显示在界面上的任务就不见了 ... 我们需要在应用视图的初始化的方法里面,去把存储在 Local Storage 里的模型提取出来 ...

视图

回到应用的视图 ... 在 initialize 这个方法里面,使用集合的 fetch() 方法,可以从数据源那里把模型提取出来,再放到集合里面 ... 这样,也会去触发集合的 add 事件 ... 发生这个事件,就会去执行 addOne 这个方法 ... 也就会把任务模型显示到界面上 ...

app.todoList.fetch();

保存 ... 再回到浏览器 ... 刷新 ...

在这里,会显示出存储在 Local Storage 里面的所有的任务项目 ...

来自  https://ninghao.net/video/1522#info

13)回顾

现在,我们就完成了这个任务管理应用里面的一个基本的功能。在文本框里输入任务的标题 ... 按一下回车 ... 会存储任务,并且,会把任务显示到界面上 ...

下面可以再一起回顾一下我们都做了什么。

模型

首先,我们定义了一个表示任务项目的一个模型 ... 在 Models 目录的下面,Todo.js 这个文件里面。

这里我们用 Todo 这个词来表示这个任务的模型 .. 在这个模型里面,设置了两个基本的属性 ... title 表示任务的标题,completed 表示任务的完成的状态 ...

集合

接着我们又添加了一个集合,来表示任务的列表 ... 在 Collections 这个目录的下面,TodoCollection.js 这个文件里面 ...

这个集合的名字是 TodoCollection ... 在这里指定了跟这个集合对应的模型是什么 ... 然后设置了一下 Backbone 的 Local Storage 插件 ... 它的作用就是,把数据放到浏览器的 Local Storage 里面去存储 ...

这里,又新建了一个集合的实例 ... 叫 todoList ... 在应用的其它的地方,会用到这个 todoList 这个集合的对象 ...

视图

模型需要一个视图去组织它的显示 .. 在 Views 这个目录里面的 TodoView.js 这个文件里,我们为任务模型定义了一个叫 TodoView 的视图 ... 在这里,指定了视图的标签是 li ,一个列表标签 ...

然后给任务模型创建了一个模板 ... 模板是在 index.html 这个文件里面定义的 ... 就是这个 item-template ...

应用视图

我们又创建了一个应用的主视图,在 Views 这个目录里面的 AppView.js 这个文件里面。 设置了一下它的 el 属性 ... 这里我们用的是一个现成的元素 ... 也就是在 index.html 里面的 id 是 todoapp 的这个元素 ...

在这个视频里面,监听了文本框的 keypress 事件,也就是一个按键事件 ... 发生这个事件的时候,如果用户按的是回车键 ... 并且文本框里有东西的话 ...

就会根据用户输入的东西,去创建了一个任务项目 ... 同时也会把这个任务项目存储到 Local Storage 里面,并且会把这个任务项目添加到任务的集合里。

这样,又会触发 add 事件,我们又监听了这个事件,发生它的时候,去执行 addOne 这个方法 ... 它做的事,就是根据创建的任务模型,去创建一个视图,然后去渲染这个任务视图,也就是会用设计好的模板,组织好任务数据的结构,再把它追加显示在应用的界面上。

在初始化这个应用视图的时候,还会去调用任务集合的 fetch() 方法,从数据源那里提供出任务的模型,并且把它们都添加到集合里面,这样也会触发 add 事件,然后把这些任务项目显示到应用的界面上。

来自  https://ninghao.net/video/1523#info

切换状态

14)切换任务的完成状态 - 思路

点击这个任务列表里面的某个任务项目旁边的这个 对号 ... 可以切换当前这个任务的完成的状态 ... 这个对号其实就是一个复选框 .. 只不过上面加上了一些额外的样式,让它看起来是这个样子 ...

打开 index.html ... 在这个应用的主页面上,为 任务模型 定义了一个模板 ... 就是这个 item-template ... 这里的这个 带 toggle 类的 checkbox 类型的 input 元素,就是我们刚才看到的那个切换任务状态的对号 ...

在这里, 我们用了一组 Underscore 模板的特殊标记 ... 在它里面,先去判断一下模型的 completed 属性的值 ... 如果它的值是 true 的话 ... 就会在这个 checkbox 元素上添加一个 checked 属性 ... 表示勾选了这个复选框 ..

我们可以去监听这个复选框的点击的事件 ... 也就是,如果有人点了复选框 ... 去执行一个设计好的函数,可以用来切换任务的状态,其实就是去修改任务的 completed 属性的值。

来自  https://ninghao.net/video/1542#info

15)切换任务的完成状态 - 实施


下面,我们先到任务的模型里面,去添加一个方法,用这个方法,可以修改任务的 completed 属性的值。

模型

在 js 目录,下面的 Models 这个目录里面,找到 Todo.js ... 这是我们为任务数据创建的模型。

在这里,添加一个方法 ... 可以叫做 toggle ...

toggle: function() {}

在这个方法里面,我们可以使用模型的 save 方法,去保存模型的属性的值 ... 先用一个 this.save ... 表示使用当前这个模型的 save 方法 .. 给这个方法传递一个对象 ... 然后添加一个 completed 属性 ... 它就是用来表示任务状态的那个属性 ...

然后要保存的跟这个属性对应的值 ... 应该是跟任务当前的这个属性的值相反的值 ... 也就是,如果任务当前这个属性的值是 false ,那么这里要设置的值就是 true .. 如果原来的任务的这个属性的值是 true 的值 ... 这里要保存的值就是 false ...

用一个 this.get ... 使用模型的 get 这个方法 ... 可以得到指定的属性的值 ... 要得到值的属性就是 completed ...

然后在它前面,可以加上一个 ! 号,表示跟得到的值相反的值 ...

toggle: function() {
this.save({
completed: !this.get('completed')
});
}

事件

下面,我们再去监听一下切换状态的那个复选框的点击的事件 ... 监听这个事件,可以在模型的视图里面去做 ... 因为这个复选框元素属于这个模型视图的一部分 ...

在 js 这个目录里面,找到 Views ... 然后打开这个目录里面的 TodoView.js ... 在视图里监听 DOM 上的事件 ... 可以添加一个 events 属性 ...

events: {}

然后,在这个对象里面,添加想要监听的事件 ... 这里我们要监听的是发生在复选框元素上的点击事件 ... 所以,事件的类型是 click ... 再输入一个空格 .. 然后是发生这个事件的元素 ... 在复选框上面,有一个叫 toggle 的 css 类,可以用这个类,来定位这个元素 ..

再指定一下,处理这个事件要用到的函数 ... 这里设置成 toggleCompleted ...

// 监听 DOM 事件
events: {
'click .toggle' : 'toggleCompleted'
},

在下面,再去定义这个方法 ...

它做的事很简单,就是去执行一下与视图相关的这个模型里面的 toggle 方法 ... 这个方法就是刚才我们在模型里面定义的,用来修改 completed 属性的值的那个方法 ...

// 切换任务状态
toggleCompleted: function() {
this.model.toggle();
},

保存 ... 下面到浏览器上试试 ...

测试

先打开 Chrome 的开发者工具 ... Resource 选项卡 ... Local Storage ... 找到一个任务 ... 比如 .... 这里你会看到,它的 completed 属性的值是 false ...

下面我们点击一下这个 对号 ... 再刷新一下 Local Storage ... 你会发现 ... 这条任务的 completed 属性的值,就变成了 true ...

再点一下这个对号 ... 刷新 Local Storage ... 它的 completed 属性的值,又会变成原来的 false ...

来自  https://ninghao.net/video/1543#info


16)为完成的任务添加样式


在完成的任务项目上面,会有一点额外的样式,比如把文字的颜色变成浅灰色,文字上面还有一条删除线 ... 这些样式,已经在应用的样式表里面定义好了 ... 放在了 completed 这个类里 ...

右键点击任务项目,审查元素 ... 然后在任务项目的 li 标签里面,添加一个 completed 类 ... 你会看到有一些样式,添加到了这个任务项目上面 ...

下面,我们可以想办法,把这个类,添加到是完成状态的任务项目上面 ...

在模型的视图的 render 这个方法里面,我们可以使用 jQuery 的 toggleClass 这个方法,去给完成状态的任务添加 completed 这个 css 类。

打开 js ,Views ,TodoView.js

在 render 这个方法里面 ... 输入 this.$el ... 这个 $el 是用 jQuery 包装的这个视图的 el 属性的一个简单的写法 ... 这样我们就可以使用其它的方法去处理视图的 el 属性 。

这里我们可以使用 jQuery 的 toggleClass ... 这个方法,可以添加或者删除掉指定元素里面的 css 类 ... 在这里,就是 completed 这个类 ...

toggleClass 这个方法还支持一个开关的参数 ... 这个开关决定了是要添加这个 css 类,还是删除掉这个 css 类 ... 如果这个开关返回的值是 true ,就去添加前面这里指定的这个 css 类,如果切换器返回的是 false ,就从元素里面,移除掉这个 css 类。

在这里,我们可以用任务的 completed 这个属性的值作为这个方法的开关 .. 得到视图对应的模型的属性的值,可以这样 ...

this.model.get('completed')

这样,如果模型的 completed 属性的值是 true 的话 .. 就会在这个模型视图的 el 元素上面添加一个叫 completed 的 css 类 ... 如果 completed 属性的值是 false ,就会去掉 completed 这个 css 类。

测试

保存 ... 回到浏览器 ...

先刷新一下 ... 然后找一下任务项目 ... 点击这个对号 ... 这样会把这个任务的 completed 属性的值设置成 true .. 然后刷新一下 ... . 你会看到,在这个任务项目的上面,会使用一点额外的状态 ... 这就表示在它上面已经添加了 completed 这个 css 类 ...

不过我们想要的是,点击这个切换状态的复选框以后,立即看到效果 ... 在下面的视频里,我们再去看一下解决的办法。

来自  https://ninghao.net/video/1544


17)当任务发生变化时重新渲染并显示 - change 事件


点击这个对号以后,让任务的显示立即做出反应 .... 解决这个问题的办法就是,在这个任务视图上面,去监听一下模型的 change 事件 ... 因为点击这个切换状态的复选框以后,会改变模型的属性的值 ... 这样就会触发 change 事件 ...

当发生这个事件的时候,可以再去执行一下视图的 render 方法,重新渲染一下 ... 这样就会在任务项目上面立即添加或者移除掉 completed 这个 css 类。

视图

再回到 TodoView 这个视图 ... 然后在这里面,去添加一个初始化的方法 .. 也就是 initialize 这个方法 ...

initialize: function() {}

然后在这个方法里面,去监听一下模型的 change 事件 ... 可以使用视图的 listenTo 这个方法去监听模型的 change 事件 ... 先设置一下发生事件的对象 ... 这里就是 this.model ,表示当前这个视图对应的模型 ... 第二个参数是要监听的事件的类型,这里是 ... change ... 然后,第三个参数是发生这个事件以后要做的事情。

这里,我们用一个 this.render ,表示使用当前这个模型的 render 这个方法 ...

现在,当点击切换状态的复选框的时候,改变了 completed 属性的值,发生了 change 事件,就会重新去渲染一下显示 ... 然后为发生变化的任务项目添加或者删除掉 completed 这个 css 类。

测试

保存一下 ... 再回到浏览器 ... 先刷新一下 ...

然后点击任务项目左边的切换状态的这个对号 ... 任务项目的显示,会立即做出反应 ...

// 初始化
initialize: function() {
this.listenTo(this.model, 'change', this.render);
},


来自  https://ninghao.net/video/1545#info

编辑任务

18)编辑任务


编辑任务的功能是这样的,我们可以先来看一下完成之后的效果 ... 双击想要编辑的任务的标题 ... 会在当前的这个位置上显示一个文本框 ... 在文本框里已经填好了任务当前的标题 ... 然后再去修改一下这个任务的标题 ...

按下回车键 ... 可以保存所做的修改 ... 或者,也可以在修改完成以后,点一下其它的地方 ...

这样也可以保存对任务的修改 ...

再双击一下 ... 这里,如果我们删掉任务里面的所有的内容 ... 按下回车,或者点一下其它的地方 ... 这样会删除掉这条任务 ...

实现这些功能,可以先在任务的显示模板里面去添加一个文本框元素,默认会隐藏这个元素,在这个文本框元素里面,已经填好了任务的标题 ... 当用户双击任务标题的时候,会把这个隐藏的带有任务标题的文本框元素显示出来 ...

然后可以监听这个文本框的事件,当用户按下回车键,或者离开这个文本框的时候,会保存对任务的修改 ... 如果清空了任务的标题 ... 回车或者离开文本框以后,会调用模型的 destroy 方法,把这个模型删除掉 ... 同样也会把它从从界面上删除掉 ...

来自  https://ninghao.net/video/1633#info


19)双击任务标题以后显示编辑任务的文本框 - dblclick 事件

我们先去给任务视图里用到的模板添加一个用来编辑任务的文本框元素 ... 打开 index.html ... 找到定义 item-template 这个模板的地方 ...

在这组带 view 类的 div 标签的下面 ... 添加一个 text 类型的 input 元素 ... 在这个元素上面,可以加上一个 css 类 ... 叫它 edit ... 在应用的样式表里,已经为这个类设计好了一些样式 ..

然后再设置一下这个文本框的默认的值 ... 添加一个 value 属性 ... 它的值,就是任务的标题 ... 用一个 underscore 模板功能的特殊的标记 ... 再加上一个 title ,表示这里应该是任务的标题属性的值 ..

<input class="edit" value="<%= title %>">

保存一下 ... 回到浏览器 ...

刷新一个应用的主页 ... 然后右键点击一个任务项目 ... 审查元素 ... 在这里,你会看到这个 input 元素 ... 在这个元素上面的 edit 这个类里面,已经定义好一些样式 ... 其中这里就有一个 display: none 的样式 .. 表示不显示这个元素 ...

在这个包装任务项目的 li 标签上,添加一个叫做 editing 类..

这样就会隐藏任务的标题,然后把那个编辑任务的文本框显示出来 ... 因为在应用的样式表里面,已经为这个 editing 类添加了合适的样式 ...

下面,我们可以到任务的视图上去监听一个双击事件 ... 发生这个事件以后,在包装任务项目的这个 li 标签上面,去添加一个做 editing 的 css 类 ...

视图

在 js 这个目录里面,打开 Views ... TodoView.js ...

在这个视图里的 events 属性里面,去监听一个双击事件 ... 这个事件是发生在 label 标签上的 ... 这个 label 标签就是包装任务标题用的那个标签 ...

发生这个事件以后,去执行 edit 这个方法 ...

'dblclick label' : 'edit'

在下面,我们再去定义这个 edit ...

edit: function() {}

这个方法做的事儿就是,在包装任务项目的元素上面,添加一个叫 editing 的 css 类 ...

this.$el .. 再用 jQuery 的 addClass 这个方法,可以给元素添加指定的 css 类 ... 要添加的类是 editing ...

this.$el.addClass('editing');

这样,会把修改任务用的那个文本框显示出来 ... 下面,我们可以让这个文本框处于一个焦点的状态 ... 找到带 .edit 类的元素,也就是那个文本框元素 ... 再用一个 jQuery 的 focus 方法 ...

this.$('.edit').focus();

测试

保存 ... 再回到浏览器 ... 先刷新一下 ...

然后,双击任务的标题 ... 这样会触发 dblclick 事件 ... 我们监听了这个事件 ... 在它发生的时候,要做的事就是在任务项目的元素上面去添加一个 editing 的 css 类 ... 这样就会把在 label 标签里面的任务标题隐藏起来 ... 把编辑任务的文本框显示在这里 ...

不过,现在它还不能保存对任务标题的修改 ... 在下面的视频里,我们再去看一下 ...


来自   https://ninghao.net/video/1634#info


20)回车或离开编辑任务的文本框时保存修改 - keypress 与 blur 事件

在前面的视频里,我们介绍过,按下按键的事件,还有怎么样去判断用户是否按的是回车键的方法 ... 在编辑任务的功能里面,同样需要用到这个类似的方法去处理一下 ...

先打开模型的视图 ... 因为这个事件是发生在模型的视图元素里面的 ...

在 TodoView.js 这里面, 找到 events 这个属性 .... 在这个属性里,可以继续去添加监听的其它的 DOM 事件。

按下按键的事件叫做 keypress ... 这个事件是发生在 .edit 这个类所在的元素上的 ... 发生这个事件的时候,去执行一下 updateOnEnter ..

'keypress .edit' : 'updateOnEnter',

在下面,再去定义一下这个方法 ...

updateOnEnter: function() {}

在这个方法里面,可以接受一个事件对象的参数 ... 这里用 event 表示 ... 然后在这个方法里面,去判断一个用户按的是不是回车键 ..

if (event.which === 13) {}

按下的按键是在事件对象的 which 属性里面,回车键的代号是数字 13 ...

如果是的话,就去执行一下另一个方法 ... 这里就是这个视图里面的 close 这个方法 ...

this.close();

在它上面,再去定义一下这个 close 方法..

close: function() {}

在这个方法里面,先去得到编辑任务的文本框里面的值 ... 先给这个值起个名字 ... 可以叫它 newTitle ... 然后找到这个视图元素里面的 .edit 这个类的元素 ... 再用 val 这个方法得到元素里面的值 ... 再调用一下 trim 这个方法,去清理一下空格 ...

var newTitle = this.$('.edit').val().trim();

然后用视图相关的模型的 save 方法 ... 去保存一下 ...

this.model.save({title: newTitle});

完成修改以后,需要把文本框上面的 editing 这个类去掉 ... 这样就又会显示出任务的标题 ... 这里我们用的是 jQuery 的 removeClass ,去移除掉元素上的指定的 css 类 ...

this.$el.removeClass('editing');

blur

最后,我们需要再去监听一下编辑任务的文本框元素的 blur 事件 ... 当元素处在焦点状态的时候,离开这个元素以后,就会触发一个 blur 事件 ...

在 events 这个属性里面 ... 另起一行 ... 要监听的 DOM 事件的类型是 blur ... 这个事件是发生在 .edit 这个类的元素上面 ...

发生这个事件以后,去执行一下 close 这个方法 ...

'blur .edit' : 'close',

测试

下面,我们到浏览器上试试 ... 先保存一下 ... 回到浏览器 ... 刷新 ..

双击要编辑的任务项目 ... 修改任务的标题 ... 然后按一下回车 ...

这样会保存所做的修改 ...

再试一下 ... 双击任务项目 ... 修改标题 ...

然后随便点一下其它的地方 .. 这样离开了文本框以后,会触发 blur 事件 ... 同样会保存对任务的修改 ...

来自  https://ninghao.net/video/1635#info

删除任务

21)删除单个的任务项目

有两种方法,可以删除掉应用里面单个的任务 ... 先看一下完成以后的项目 ... 鼠标放在任务的标题上 ... 在它的右边会显示一个叉号,点一下它 ... 可以删除掉任务 ... 被删除的任务会立即从界面上移除掉 ...

另外还有一种可以删除任务的方法 .. 双击任务的标题 ... 然后请空任务的标题 ... 按一下回车,或者点一下其它的地方 ... 这样也会删除掉任务项目。

先去实现一下用第一种方法删除单个任务的功能 ... 我们需要去在任务项目的模板里面去添加一个删除按钮 ...

先打开应用的主页 index.html

打开定义任务项目模板的位置 .. 然后在显示任务标题的这个 label 标签的下面,去添加一个 button 标签 .. 在这个标签上,再加上一个 destroy 类 ...

这就是删除任务用的按钮 ... 保存一下 ...

视图

下面,我们再到任务的视图里面去监听一下这个按钮的点击事件 ... 发生点击事件以后就去把任务模型删除掉。

打开任务的视图 ... 在 js 里面的 Views 目录下面的 TodoView.js 这个文件里面 ...

找到 events 这个属性 ...

在这里,再添加一个要监听的 DOM 事件 ... 事件的类型是 click ,点击事件 ... 这个事件是发生在 .destroy 这个类的元素上 ... 也就是那个用来删除任务的按钮 ...

事件发生以后,去执行一下 clear 这个方法 ...

'click .destroy' : 'clear'

在下面,我们再去创建这个方法 ...

clear: function() {}

它要做的事儿就是去把任务模型删除掉 ... 使用模型的 destroy 这个方法 ...

this.model.destroy();

测试

下面,我们可以去浏览器上试一下 .. 先保存 ... 回到浏览器 ..

打开应用的首页 ... 刷新 ..

鼠标放到任务标题的上面 ... 会显示出一个删除按钮 ... 这个按钮的样式已经在应用的样式表里定义好了 ...

点击这个按钮 ... 会删除掉当前的这条任务 ...

不过在应用的界面上,没有立即做出反应 ... 我们需要再刷新一下 ..

在下面的视频里,我们可以改进一下这个删除的功能。点击删除按钮以后,会在界面上立即做出反应,把删除掉的任务从界面上移除掉。


来自  https://ninghao.net/video/1649#info

22)删除以后立即把任务从界面中移除掉 - destroy 事件

在把模型删掉以后,在模型上,会触发一个 destroy 事件。利用这个事件,我们可以在删掉任务模型以后,立即去做一点事情,比如把任务项目从界面上移除掉。

打开任务的视图 ... 在 initialize 这个方法里面,可以去监听一个模型的这个 destroy 事件 ..

用视图的 listenTo 这个方法 ... 先设置一下发生事件的对象 ... 这里就是视图相关的那个模型 .. 可以用 this.model 来表示 ...

第二个参数是事件的名字 ... 这里就是 destroy ... 最后再设置一下处理这个事件要用到的方法 ... 我们可以使用视图的 remove 方法。 这个方法,会把被删除掉的任务从界面上删除掉。

保存一下 ... 回到浏览器 ... 刷新 ...

鼠标放在要删除的任务项目上 ... 然后点击 删除 按钮 ...

这样会把任务项目删除掉 ... 同时会立即把它从界面上删除掉。因为在删除的时候发生了 destroy 事件,我们监听了这个事件,处理这个事件要做的事就是从界面上移除被删除掉的任务。

来自  https://ninghao.net/video/1650#toc

23)删除单个任务的另一种方法


下面,我们可以再看一下删除单个任务的第二种方法 ... 就是双击编辑任务 ... 清空任务的内容 ... 回车以后,也会删除掉任务 ...

回到任务的视图 ...

找到这个 close 方法 ... 这个方法现在的作用就是去保存修改的任务 ... 在这里,可以去判断一下得到的这个任务的内容 ... 如果有内容,就保存一下,如果没有内容的话,就去执行一下 clear 方法,把这条任务删除掉 ...

if (newTitle) {
this.model.save({title: newTitle});
} else {
this.clear();
}

if ... 判断的条件就是 newTitle ... 条件返回真,就去保存任务 ... 再加上一个 else ... 不然的话 ... 就去执行这个视图里面的 clear 这个方法 ... 这个方法就是去把任务删除掉。

保存 ... 再到浏览器上去试试 ... 刷新 ...

双击编辑任务 ... 然后清空一下里面的东西 ...

按一下回车,或者点一下其它的地方 ... 这样都会去执行视图里面的 close 方法 ...

在这里,会删除掉这个空白的任务 ...


来自  https://ninghao.net/video/1651#info

状态栏

24)显示还剩多少个任务与删除完成的任务按钮

接下来我们要完成的功能就是在这个应用的底部,有一个状态栏,在这上面,可以还剩下多少个没有完成的任务的提示 ... 在这个状态栏的右边 ..

还有一个可以清空所有完成的任务的按钮 ... 在这个按钮上面,会显示出已经完成的任务有多少个 .. 点击这个按钮可以清除掉所有的已经完成的任务。

点击任务项目左边的切换状态的复选框 ... 注意这个提示里的内容会实时的更新 ...

实现这两个小功能可以这样。

在任务的集合里面,去创建两个方法,一个可以得到完成状态的任务,另一个方法可以得到没完成的任务。在到应用的主页 index.html 里面去设计一个显示这些内容的模板。

最后可以在应用的视图里面去得到完成的还有剩下的任务的数量,再把它们填充到模板里面显示出来。然后去监听清除完成任务的按钮的点击事件,发生这个事件的时候,去调用方法逐个删除掉已经完成的任务项目。

来自  https://ninghao.net/video/1664#info

25)得到完成的任务与未完成的任务

Backbone 和 Undersocre 是紧密结合到一块儿的,所以,我们可以在 Backbone 的集合里面, 去使用 Underscore 提供的方法去处理集合里面的项目。比如为集合里面的每一个项目去应用一个函数,按指定的条件过滤一下,找出包含特定属性的项目等等。

下面,我们可以到应用的任务的集合里面,去添加两个方法,用它们来得到完成的还有未完成的任务。

在 js 这个目录下面,找到 Collections ... 打开 TodoCollection.js ... 在这个文件里定义就是跟任务模块相关的集合 ...

在这里,先去定义一个可以得到所有的完成状态的任务 ...

这个方法可以叫做 getCompleted ... 它做的事,就是去返回找出来的状态是完成的任务 .. 先用一个 return ... 然后使用 this.filter ... this 表示当前的这个集合,也就是包含任务项目的这个集合 ...

filter 是在 Underscore 这个类库里面提供的一个方法 ... 它的作用就是可以筛选出列表里面的指定的条件是真的项目 ...

给这个 filter 方法一个函数 ... 在这个函数里面,可以返回要过滤的条件 ... 这个函数可以接受一个参数 ... 这个参数就是表示列表里面的单个项目用的 ... 我们可以用 todo ,来表示在集合里面的单个的任务项目 ..

然后在这个函数的里面,去返回一个条件 ... 先用一个 return ... 然后是 todo.get('completed') .. 使用模型的 get 方法,得到模型里面的 completed 这个属性的值 ...

这样过滤出来的就是在集合里面的,所有的 completed 属性的值是 true 的项目 ... 也就是,状态是完成的任务项目 ...

getCompleted: function() {
return this.filter(function(todo) {
return todo.get('completed') === true;
});
},

下面,我们需要再去定义一个可以得到没有完成的任务的方法 ... 这个方法可以叫做 getRemaining ..

在这个方法里面,我们同样可以使用 Underscore 的 filter 方法,过滤出模型的 completed 是 flase ,也就是未完成的任务项目。使用这个集合的 filter 方法 ... 把一个函数交给这个方法 ... 在这个函数里面,用 todo 表示集合里面的单个的项目 .. 再返回模型的 completed 属性的值是 flase 的项目 ..

getRemaining: function() {
return this.filter(function(todo) {
return todo.get('completed') === false;
});
}

保存一下 ... 下面,我们去浏览器里预览一下 ...

刷新 ... 打开控制台 ... 先试一下集合里的 getCompleted 这个方法 ...

app.todoList.getCompleted()

这里给我们返回来 x 个项目 ... 打开看一下 ... 打开 attributes 这个属性 ... 你会发现 ... 所以返回来的任务项目的 completed 这个属性的值 ... 都会是 true ...

再去试一下 ... getRemaining 这个方法 ...

app.todoList.getRemaining() ...

同样也会给我们返回几个项目 ... 找到这些项目的 attributes 属性 ...

在这里,你会看到,这些项目的 completed 这个属性的值,都是 false ...

来自  https://ninghao.net/video/1665#info


26)状态栏模板

这个状态栏的显示,我们可以去设计一下模板。先打开应用的主页 ... index.html ... 在这个页面上,我们再去定义一个模板 ...

用一组 script 标签 ... 上面加上一个 type 属性 ... 属性的值是 text/template ... 表示这里面的内容是一个模板 ... 并不是 JavaScript 代码。所以浏览器不会去解释它。

在这组模板的 script 标签里面,再加上一个 id 属性 ... 去给这个模板起个名字 ... 这里我们叫它 stats-template ..[stæts]

script[type="text/template"]#stats-template

在这个模板里面,先去设计一下显示还剩多少个任务的提示 ... 这个提示信息的周围可以用一组 span 标签 ... 上面加上一个 id ,叫做 todo-count ... 这个 id 的样式已经在应用的样式表里设计好了。

在这个标签里面,输入点文字 ... 还剩 .. 个任务 ... 这中间再用一个 Underscore 模板的特殊标记 ... 特殊标记里的东西是 remaining ... 它的值等会儿会在应用的视图里面给它传递进来。

<%= remaining %>

清除按钮

在它下面,再去添加一下用来清除所有完成任务的按钮 ... 这个按钮的显示可以用一个 if 去判断一下 ... 也就是,如果应用里面有已经完成的任务,才会显示出这个按钮 ...

<% if (completed) { %>
<% } %>

在这个 if 里面, completed 的值,会在应用的视图里给它传递进来 ... 注意在这个判断语句周围用的这个特殊的标记里面,开始的标记这里,没有等号 ... 因为我们并不是想要输出东西 ... 而是去执行代码 ...

在它们中间,再去添加一个按钮 ... 一个 button 标签 ... 上面加上一个叫 clear-completed 的 id ,同样这个 id 的样式已经在应用的样式表里设计好了。

再加上点文字 ... 这里的这个 completed 也会在应用的视图里面给它传递进来。

清除已完成 (<%= completed %>)

这样我们就定义好了状态栏上显示的模板,在后面的视频里, 我们再看一下怎么样在应用的视频里面去使用它。

来自  https://ninghao.net/video/1666#info

27)在应用的视图里使用显示任务状态栏的模板

下面,我们可以在应用的视图里面,去使用之前定义的状态栏的模板。打开 js ,Views 目录下面的 AppView.js ...

在这里,可以先去定义一个用 Underscore 的模板功能处理好的模板的方法 ... 可以叫做 statsTemplate ... 表示状态栏的模板 ...

然后用 Underscore 的 template 方法 .... 在这个方法里,要把定义的模板交给它 ...

用 jQuery ... 找到 stats-template 这个 id 的元素 ... 再用 html 这个方法,可以得到这个元素里的内容 ... 也就是在上一个视频里,我们定义的状态栏的模板的结构 ...

statsTemplate: _.template($('#stats-template').html()),

在下面, 再去定义一下,视图的 render 方法 ...在这个方法里需要去设置一下在状态栏上显示的内容 ...

render: function() {}

用一个 this.$('#footer') ... 找到当前视图元素里面的带 footer 这个 id 的元素。也就是用来显示应用的状态栏的那个元素 .. 可以先去看一下 ... 打开 index.html ..

在这里 ... 有一个带 footer 这个 id 的元素 ...

再回到应用的视图 ... 然后去设置一下 footer 这个元素里面的内容 ... 用 html 这个方法 ... 要设置的内容,就是用前面定义好的 statsTemplate 这个方法处理的内容 ...

我们需要给这个方法传递两个东西 ... 一个是完成的任务的数量 ... 还有一个是未完成的任务的数量 ...

因为在状态栏的模板里面需要这两样东西 ...

在上面,先去得到这两个属性的值 ... 先定认一个变量来存放完成的任务的数量 ... 它的值,就是完成的任务的数量 ... 集合里的 getCompleted 这个方法,可以得到所有的已经完成的任务项目 ... 调用这个结果的 length 属性,得到的就是完成的任务项目的数量 ...

var completed = app.todoList.getCompleted().length;

另起一行 ... 再定义一个变量 .. 叫做 remaining ... 它的值,是用集合里的 getRemaining 这个方法得到的未完成的任务项目的数量 ...

var remaining = app.todoList.getRemaining().length;

再回到这个 statsTemplate ... 给它一个对象参数 ... 然后设置一个在模板里面的 completed 还有 remaining 这两个东西 ....

completed: completed,
remaining: remaining

它们的值,就是我们在上面得到的完成的还有未完成的任务项目的数量。

监听事件

下面,我们要考虑一下,在什么情况下,去执行这个 render 方法 ... 因为要这个状态上要实时去显示完成的还有未完成的任务项目的数量 ... 所以,当我们切换了任务项目的状态或者删除了任务项目以后,要立即去更新一下状态栏的显示 ...

这样我们就可以去监听一下在任务集合里发生的任何的事件 ... 发生事件以后,就去执行一下应用视图里的 render 方法,更新一下状态栏的显示 ...

在视图的 initialize 这个方法里面 ... 用 listenTo ,去监听一个 all 事件 ... 事件是发生在 app.todoList 上面的 ... 它表示的就是应用的集合 ... 事件的名字是 all ,发生任意的事件都会触发这个 all 事件 ... 处理它的方法就是 this.render ... 使用这个视图里面的 render 方法 ...

this.listenTo(app.todoList, 'all', this.render);

下面,我们到浏览器上去试一下 ...

测试

保存 ... 回到浏览器 ... 刷新 ....

因为在应用视图初始化的时候,调用了集合的 fetch 方法,把模型提取出来,放到集合里面,这样会触发集合的 add 事件,同时也就会发生一个 all 事件 ... 发生这个事件,要做的事儿,就是去执行一下应用视图的 render 方法 ... 在这个方法里面,先会得到完成与未完成的任务的数量 ... 然后再把它们显示到应用的页脚这里 ...

你会看到,现在这里显示,还剩 x 个任务 ... 右边这里 ... 会显示完成的任务项目数 ...

下面,我们可以找到一个任务项目 ... 点一下它旁边的切换状态的对号 ...

你会看到,在状态栏上,会实时的显示出计算出来的结果 ...

来自   https://ninghao.net/video/1667#info

28)清除所有已完成的任务

点击应用右下角的这个清除按钮 ... 可以删除掉所有的已经完成的任务项目 ... 在这个视频里我们去实现这个功能 ... 先去监听一下发生在这个按钮上面的点击事件 ... 发生点击事件以后,去执行一个函数 ... 在这个函数里面,去对所有的已经完成的任务,调用一个 destroy 方法,把它们挨个删除掉 ...

因为这个按钮元素是应用的视图的一部分 ... 所以,我们可以在应用的视图里面,去监听它的点击事件 ...

打开 AppView.js ...

监听的 DOM 事件,可以放在视图的 events 这个属性里面 ...

事件的类型是 click ... 事件是发生在删除按钮上的 ... 在这个按钮上有一个叫 #clear-completed 的一个 id ... 在这个按钮上,发生点击事件以后,去执行一下 clearCompleted 方法 ...

'click #clear-completed' : 'clearCompleted'

在下面, 再去定义一下这个方法 ...

clearCompleted: function() {}

在这个方法里面 ... 我们需要对每一个状态是完成的任务项目应用一个 destory 方法,把它们删除掉 .... 在这里,可以使用 Underscore 里面的 invoke 这个方法 ... 它可以为指定的列表里面的每一个项目,去应用一个方法 ...

_.invoke ... 先要给它指定一个列表项目 ... 这个列表就是状态是完成的任务 ... 之前我们在集合里面,定义了一个叫 getCompleted() 的方法,它可以得到已经完成的任务 ...

对这个列表里面的每一个项目去应用的方法就是这个项目的 destory 方法 ...

_.invoke(app.todoList.getCompleted(), 'destroy');

另起一行 ... 再用一个 return false;

测试

下面, 我们再去测试一下 ... 保存 .... 回到浏览器 ...

刷新 ...

标记几个任务为完成的状态 ....

注意在状态栏上会实时显示结果 ...

然后点击 清除已完成 ...

这样会把应用里面,所有的状态是已完成的任务项目都删除掉 ...

来自 https://ninghao.net/video/1668#info

29)切换所有任务的状态

在创建新任务这个文本框的旁边,还有一个复选框 ... 它可以切换应用里面,所有的任务的状态 ... 勾选上,可以把它所有的任务标记为完成的状态 .... 再点一下,可以把所有的任务标记为未完成的状态 ...

我们可以先去监听这个复选框的点击事件 ... 然后根据复选框的 checked 这个属性的值,去设置每个任务项目的 completed 属性的值 ...

先回到应用的视图 ...

然后在 events 这个属性里面,去监听一下切换全部任务状态的那个复选框的点击事件 ... 监听事件类型是 click ... 发生这个事件的元素是 #togle-all 这个 id 的元素 ...

事件发生以后,去执行一下 toggleAll 这个方法 ...

'click #toggle-all' : 'toggleAll'

在下面,再去定义这个方法 ...

toggleAll: function() {}

在这个方法里面,我们可以先得到那个切换全部状态的复选框的 checked 属性的值 ... 如果勾选了复选框,这个属性的值就是 true ,没勾选的话,这个属性的值就是 false ,我们可根据它的值,去设置全部的任务的状态。

var completed = $('#toggle-all')[0].checked;

另起一行 ... 需要再想办法处理集合里的每一个项目 ... 我们可以使用 Underscore 的 each 这个方法 ...

app.todoList.each ... 给这个方法一个函数 ... 这个函数就是处理集合里面的每个项目用的 ... 给这个函数一个参数 ... 可以用 todo 表示集合里面的单个的项目 ...

在这个函数里面,调用任务模型的 save 方法 ... 去保存一下 ... 给这个方法一个对象 ... 要保存的就是任务模型的 completed 这个属性的值 ...

保存成的值,就是根据切换全部任务状态的复选框来决定的 ... 在上面,我们已经把这个复选框的 checked 属性的值,交给了一个变量,叫做 completed ...

toggleAll: function() {
var completed = $('#toggle-all')[0].checked;
app.todoList.each(function(todo) {
todo.save({
'completed' : completed
});
});
},

保存 .. 回到浏览器 ... 刷新 ... 点击一个切换全部状态的复选框 ...

勾选了这个复选框 ... 会把所有的任务的状态都标记成已完成的状态 ....

取消勾选它 ... 会把所有的任务的状态标记成未完成的任务 ...

来自  https://ninghao.net/video/1669#info

切换显示

30)切换显示不同状态的任务列表

点击 进行中 ,会把 completed 属性的值是 true 的任务隐藏起来,也就是完成状态的任务。 在这个页面上,把任务的状态标记成已完成,可以立即把这个任务隐藏起来。

打开 已完成 这个地址,可以把未完成的任务隐藏起来。其实就是在未完成的任务项目上,添加了一个叫 hidden 的 css 类 ... 这个类的样式就是把元素从界面上隐藏起来。

在这个小功能里,我们用到了 Backbone 的路由器 ... 点击打开设置好的地址以后,让路由器在应用的集合里面,去触发一个自定义的事件 ... 然后在应用的视图里,去监听这个事件 ...

这样点开某个地址以后,触发了这个自定义的事件,就去在相应的任务项目上添加一个 hidden 类,把它隐藏起来。下面, 我们再一步一步去实现这个功能。

来自 https://ninghao.net/video/1683#info

31)模板

在这个应用的状态栏上面,用来切换显示任务的这个导航,需要一点 html 代码,我们可以把这个简单的导航放到页脚的模板上 ...

打开 index.html ... 打开定义页脚模板的地方 ... 用一组 ul 标签 .. 去定义一个无序的列表 ... 在这个标签上面,加上一个叫 filter 的 id ...

在它里面加上几个列表标签 ... 这些列表里面的东西就是链向对应的地址的链接 ... 第一个列表项目是链向应用首页的链接 ... 地址可以设置成一个 # 号 ... 链接上面的文字是 全部 ...

第二个列表项目里,链向的是显示未完成任务项目的地址 ... 这个链接的地址可以是 #/active ... 在这里,active 就是这个地址的的一个参数 ... 在后面我们会用到它 ... 链接上的文字是 进行中 ...

第三个列表项目可以是一个链向完成的任务项目的页面地址 ... 链接的地址是 #/completed ... 在这个地址,completed 是这个地址的一个参数 ... 同样我们会在后面用到它来判断要显示的任务项目 ... 链接上的文字是 已完成 ...

保存 ... 在浏览器里预览一下 ...

在状态栏上,会显示一个简单的导航 ... 上面有三个链接 ...

点击进行中 ... 访问的地址是 #/active ... 点击 已完成 .. 访问的地址是 #/completed ...

现在,打开这些地址,还不能去做什么 ... 在下面的视频里,我们再去创建一个路由器 ... 去处理一下访问这些地址的行为 ...

来自  https://ninghao.net/video/1684#info

32)路由器

下面,我们去给应用创建一个路由器 ... 用它可以处理用户访问的地址 .. 在 js 这个目录的下面 ... 找到 Routers 目录 ... 打开 Router.js ...

在这个文件里去定义一个路由器 .. 先去添加一个 app 对象 ... 作为一个命名空间 ..

var app = app || {};

然后把要定义的路由器作为这个对象的一个属性 ... 可以叫做 TodoRouter ... 在 Backbone 里定义路由器,可以让它等于 Backbone 的 Router 的 extend 这个方法 ...

app.TodoRouter = Backbone.Router.extend({});

在这个路由器里,添加一个 routes 属性,去添加应用的路由 .. 这个路由的地址可以是 *filter ,表示这是一个splat 类型的地址 .. 这个形式的地址可以匹配任意的参数 .. . filter 就是我们为在地址上的参数起的一个名字...

再去给这个路由指定一个处理它的方法 ... 这个方法可以叫做 setFilter ...

在下面,再去定义一下这个方法 ...

setFilter: function(filter) {}

地址上的参数可以作为这个方法的一个参数传递进来 ... 我们可以用 filter 来表示 . ..

先在方法里面,去判断一下 filter 这个参数 ... 如果在地址上有参数的话 ... 可以让 filter 等于清理之后的这个参数的值 ... 可以用 trim() 这个方法清理掉多余的空格 ...

if (filter) {
filter = filter.trim();
}

因为在应用的其它地方,我们需要用到这个地址上的参数的值,所以,可以在 app 这个对象里面,再去添加一个属性 ... 可以叫做 TodoFilter .. 让它的值等于 filter .. 或者是一个空白的字符 ...

app.TodoFilter = filter || '';

为了可以让应用的其它的部分知道应用的地址发生了变化 ... 在这个路由的方法里面,可以在应用的集合上去触发一个自定义的事件 ... 然后在应用的视图里面,我们可以去监听这个事件 ... 触发自定义的事件,可以用 trigger 这个方法 ...

app.todoList.trigger('filter')

触发的这个自定义的事件的名字叫 filter ...

下面,我们再测试一下设置的这个路由是否起作用 ..

在这个方法里,用一个 console.log ,在控制台上输出点东西 ... 可以去输出地址上的参数 ... 这个参数的值,我们已经交给了 app 对象的 TodoFilter 这个属性了 .. 所以,可以把它输出到控制台上 ..

console.log(app.TodoFilter);

然后在下面,再去创建一个这个路由器的实例..

app.todoRouter = new app.TodoRouter;

最后再去执行一下 Backbone 的 history 的 start 方法..

Backbone.history.start();

保存 ... 再到浏览器上去试试 ...

刷新 .. 打开控制台 ...

然后点击导航栏上面的链接 ... 你会看到在控制台上,会输出在地址上面的参数 ... 点击 进行中 ... 会输出 active ....

再点一下 已完成 ... 会输出 completed ..

来自  https://ninghao.net/video/1685

33)利用自定义事件


下面打开应用的视图,在这里,可以去监听一下地址发生变化的时候,在集合里面发生的一个自定义的事件 .. 这个事件就是 filter ...

在视图的初始化的方法里面,使用视图的 listenTo 这个方法 . ... 这个事件是发生在集合上面的 ... 用一个 app.todoList ... 表示应用的集合 ... 再指定一下要监听的事件的名字 ... 就是 filter ...

这个事件发生以后 .. 去执行一下这个视图里的 filterAll 这个方法 ...

this.listenTo(app.todoList, 'filter', this.filterAll);

在下面, 我们再去定义一下这个方法 ...

filterAll : function () {
app.todoList.each(this.filterOne, this);
},

在这个方法里面,可以去集合里的每一个项目都去应用一个方法 ... 这里,可以使用 Underscore 里的 each 这个方法 ...

用 filterOne 这个方法,去处理一下 ... 每个项目 .. 再设置一下这个方法的上下文 ... 用一个 this ...

在它上面,再去定义这个 filterOne 这个方法 ... 可以把要处理的单个任务项目交给这个方法 ... 用 todo 来表示 .. 在这个方法里面,我们再用一下 trigger 方法,在模型上面,去触发一个自定义的事件 .. 这个事件叫做 visible ...

filterOne : function (todo) {
todo.trigger('visible');
},

模型视图

下面, 我们再到模型的视图里面,去监听这个发生在模型上的 visible 这个自定义的事件 ... 打开 TodoView.js ..

在初始化的方法里 ... 用视图的 listenTo 这个方法 ... 事件是发生在跟这个视图相关的模型上的 ... 用 this.model 来表示 ..

监听的事件是 visible ... 事件发生以后,用 toggleVisible 这个方法去处理一下 ...

在下面,再去定义这个方法 ...

toggleVisible: function() {}

这个方法要做的事儿,就是去为相应的项目添加 hidden 这个 css 类 ... 用 this.$el ,表示当前的视图的 el 元素 ... 调用 jQuery 的 toggleClass 这个方法 ... 它可以切换在元素上面的 css 类 ... 要切换的 css 类是 hidden ...

然后我们要给这个方法一个开关 ... 如果这个开关返回的是 true ,就在元素上添加 hidden 这个类 ... 如果开关返回的是 false .. 就在元素上去掉 hidden 这个类 ..

this.$el.toggleClass( 'hidden', this.isHidden());

这个开关,我们可以再用一个方法去返回值 ... 在这个方法里,我们需要用到一些逻辑运算符,在下面的视频里,我们再一起来看一下 ...

来自  https://ninghao.net/video/1686#info

34)判断是否要隐藏任务项目

下面,我们去定义这个用来返回 true 或者 false 的方法,也就是这个作为 toggleClass 这个方法的开关的这个方法。

当它返回 true 的时候,就会在元素上添加在 toggleClass 里面指定的 css 类,如果是 false ,就会从元素上去掉这个指定的 css 类。

我们希望在访问 active 这个地址的时候,把 completed 属性的值是 true ,也就是完成状态的任务,在它们上面去添加这个叫 hidden 的 css 类。

因为这个地址上显示的都是进行中的任务。

然后在访问 completed 这个地址的时候,如果在完成状态的任务项目上有 hidden 这个类的话,就把它去掉 ... 然后把这个类加到所有的进行中的任务项目上面。

在这个方法里在,我们先去得到任务项目的 completed 属性的值 ..

var completed = this.model.get('completed');

然后在它下面,用一个 return ... 去返回 true 或者 false ...

return (
(!completed && app.TodoFilter === 'completed')
||
(completed && app.TodoFilter === 'active')
);

这里我们设置了两种情况,用一个 或逻辑运算符连接 ... 用这个运算符连接的两个表达式,如果计算出来的都是布尔值 ... 这样,两个表达式有一个返回 true 的话,整个表达式就会返回 true ... 如果两个表达式返回来的都是 false ,这个整个表达式就会返回一个 false ...

在第一个表达式里面,前面这块,用了一个 ! 号,它是一个否定的逻辑 ... 它后面这个东西如果计算出来的结果可以认为是 true 的话,加上这个 ! 号就会返回一个 false ... 如果它后面的东西计算出来的东西认为是 false 的话,那么加上这个 ! 号,就会返回一个 true ...

后面,又用了一个 逻辑与 的运算符 ... 用它连接的两个东西,如果第一个东西返回 false ,就会返回 false ... 不然的话,就会返回第二个东西 ...

这里又判断了一个,前面这块计划出来的结果是否等于 completed ... 这里的 app.TodoFilter 表示的就是当前地址上的参数 ...

假设

下面, 我们假设一个任务项目的状态是完成的,也就是它的 completed 属性的值是 true ,并且,当前访问的地址是应用的首页 ...

在这个用逻辑或连接的两个表达式里的第一个表达式里面,首先,让这个 completed 属性的值变成了 false ... 这样用逻辑与连接的两个东西就会返回这个 false ... 然后再进入到第二个表达式里面,completed 的值是 true ,所以会返回第二个表达试 ... app.TodoFilter 当前的值是空的 ... 因为我们访问的页面是应用的主页 ... 这个地址上没有参数的值 ...

所以,在这里计划出来的结果也就不会等于 active ,这样就会返回 false ... 两个用逻辑或连接的表达式都返回 false ... 整个表达式就会返回 false ...

这样,这个 isHidden 这个方法返回的值就会是 false ... 在 toggleClass 这个方法里面,如果开关返回 false,就会从元素上去掉这个指定的类 ...

也就是,完成状态的任务,在应用的主页上,如果它上面有 hidden 这个类的话,就会去掉它。

下面, 我们再分析一种情况,这次我们假设任务项目的状态是未完成的 ... 并且访问的是 active 这个页面的话 ... 会发生什么呢 ...

首先,未完成的任务的 completed 的值是 false .... 用否定逻辑运算出来的结果就会是 true ... 这样, 这个用逻辑与连接的东西就会返回第二个表达式。因为当前是 active 这个地址 ... 所以,这个 app.TodoFilter 的值就会是 active ... 判断一下,是否等于 completed 的值,结果是 false ... 因为 active 不可能等于 completed .. 所以,这个表达式就会返回一个 false ...

接着再到第二个表达式里去运算一下 .. 因为 completed 的值是 false ... 这样这个表达式也就会返回一个 false ... 用逻辑或连接的两个表达式,都返回的是 false ... 整个表达式也就会返回一个 false ..

这样也就是,在 active 这个地址上,如果任务的状态是未完成 ... 就会去掉在它上面的 hidden 这个 css 类。

如果在这个 active ,显示未完成的任务的地址上 ... 如果任务的状态为已完成 ... 也就是它的 completed 属性的值是 true ...

用否定逻辑计算以后,这个值就是 false ... 这样在第一个表达式里计算出来的结果就是 false 。再看第二个表达式 ...

completed 属性的值是 true ... 所以会用第二个表达式 ... 也就是这个 app.TodoFilter ... 因为是在 active 这个页面,所以,app.TodoFilter 的值就是 active .... active 等于 active ... 这样这个表达式就会返回 true ... 用逻辑或计算出来的结果也就会返回 true ...

这样的话,就会在这个任务项目上去添加这个 hidden 类 .... 也就会把它隐藏起来 ...

下面,在这个模型视图的 render 方法里面,我们可以再去执行一下这个 toggleVisible 方法

this.toggleVisible();

测试

保存 ... 回到浏览器 ...

刷新一下 .... 点击 进行中 ... 这样会把所有完成状态的任务隐藏起来 ...

再点一下完成 ... 会去掉完成状态的任务项目上面的 hidden 类,把它们显示出来 ... 然后在未完成的任务项目上面添加一个 hidden 类,把它们隐藏起来 ....

来自  https://ninghao.net/video/1687#info

35)激活菜单项

点击这个状态上的导航菜单 ... 可以打开对应的地址来显示任务项目 ... 我们可以为这个菜单项,根据当前页面的地址来添加一个 css 类,为这个菜单项添加点额外的样式 ... 用它可以区分一下,当前打开的地址,属于哪一个菜单项 ...

在我们可以先在全部,这个菜单项上添加一个叫 selected 的类 ... 这个类的样式已经在应用里面定义好了,就是会加粗显示文字 ...

然后用 jQuery 找到所有的菜单项上的 a 标签 ... 去掉 selected 类 .. 再根据当前的地址上的参数,去在对应的菜单项上添加一个 selected 类 ...

先打开应用的主页 index.html ... 在这里找到定义页脚模板的地方 ... 然后在这个导航上面,为全部这个菜单项上的 a 标签上添加一个 selected 类 ...

保存一下 ...

再打开应用的视图 ... AppView.js ...

在这个视图里面,找到 render 方法 ... 然后用一个 this.$ ... 在当前这个视图下面,去查找一些东西 ... 要找的元素是 #filters 这个 id 下面的 li 标签下面的 a 标签 ....

用一个 removeClass 这个方法 ... 去掉这些标签里面的 selected 这个类 ...

然后再根据当前的地址 ... 去过滤一下 ... 用 filter 这个方法 ... 指定一下要过滤的条件 ... 也就是href 这个属性的值 ... 这里用一个 #/ 后面再加上当前页面地址上的参数的值 ... 这里用 app.TodoFilter 来表示 ... 如果有参数值,就让它等于这个参数的值 ... 如果没有,就让它等于一个空白 ...

最后,再对过滤出来的这个 a 标签上面,去添加一个 selected 类 ... 用 addClass 这个方法 ...

// 导航菜单激活状态
this.$('#filters li a')
.removeClass('selected')
.filter('[href="#/' + (app.TodoFilter || '') + '"]')
.addClass('selected');

保存 ... 回到浏览器 ... 刷新 ... 当前的位置是应用的首页 ... 所以, 在 全部 这个菜单项上会添加一个 selected 的 css 类 ... 你会看到,会加粗显示这个菜单项上的文字 ...

再点一下 进行中 .... 这样会在先去掉在全部这个菜单项上的 selected 类 ... 然后在这个进行中上面添加一个 selected 类 ...

再看一下 已完成 ...

去掉了在 进行中 上面的 selected 类,把它又添加到了 已完成 这个菜单项上 ...


来自  https://ninghao.net/video/1688#info








普通分类: