AngularJS入门教程之双向绑定详解


Posted in Javascript onAugust 18, 2016

在这一步你会增加一个让用户控制手机列表显示顺序的特性。动态排序可以这样实现,添加一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完成剩下的事情。

请重置工作目录:

git checkout -f step-4

你应该发现除了搜索框之外,你的应用多了一个下来菜单,它可以允许控制电话排列的顺序。

步骤3和步骤4之间最重要的不同在下面列出。你可以在GitHub里看到完整的差别。

模板

app/index.html

Search: <input ng-model="query">
Sort by:
<select ng-model="orderProp">
 <option value="name">Alphabetical</option>
 <option value="age">Newest</option>
</select>


<ul class="phones">
 <li ng-repeat="phone in phones | filter:query | orderBy:orderProp">
  {{phone.name}}
  <p>{{phone.snippet}}</p>
 </li>
</ul>

我们在index.html中做了如下更改:

首先,我们增加了一个叫做orderProp的<select>标签,这样我们的用户就可以选择我们提供的两种排序方法。

然后,在filter过滤器后面添加一个orderBy过滤器用其来处理进入迭代器的数据。orderBy过滤器以一个数组作为输入,复制一份副本,然后把副本重排序再输出到迭代器。

AngularJS在select元素和orderProp模型之间创建了一个双向绑定。而后,orderProp会被用作orderBy过滤器的输入。

正如我们在步骤3中讨论数据绑定和迭代器的时候所说的一样,无论什么时候数据模型发生了改变(比如用户在下拉菜单中选了不同的顺序),AngularJS的数据绑定会让视图自动更新。没有任何笨拙的DOM操作!

控制器

app/js/controllers.js:

function PhoneListCtrl($scope) {
 $scope.phones = [
  {"name": "Nexus S",
   "snippet": "Fast just got faster with Nexus S.",
   "age": 0},
  {"name": "Motorola XOOM™ with Wi-Fi",
   "snippet": "The Next, Next Generation tablet.",
   "age": 1},
  {"name": "MOTOROLA XOOM™",
   "snippet": "The Next, Next Generation tablet.",
   "age": 2}
 ];

 $scope.orderProp = 'age';
}

我们修改了phones模型—— 手机的数组 ——为每一个手机记录其增加了一个age属性。我们会根据age属性来对手机进行排序。

我们在控制器代码里加了一行让orderProp的默认值为age。如果我们不设置默认值,这个模型会在我们的用户在下拉菜单选择一个顺序之前一直处于未初始化状态。

现在我们该好好谈谈双向数据绑定了。注意到当应用在浏览器中加载时,“Newest”在下拉菜单中被选中。这是因为我们在控制器中把orderProp设置成了‘age'。所以绑定在从我们模型到用户界面的方向上起作用——即数据从模型到视图的绑定。现在当你在下拉菜单中选择“Alphabetically”,数据模型会被同时更新,并且手机列表数组会被重新排序。这个时候数据绑定从另一个方向产生了作用——即数据从视图到模型的绑定。

测试

我们所做的更改可以通过一个单元测试或者一个端到端测试来验证正确性。我们首先来看看单元测试:

test/unit/controllersSpec.js:

describe('PhoneCat controllers', function() {

 describe('PhoneListCtrl', function(){
  var scope, ctrl;

  beforeEach(function() {
   scope = {},
   ctrl = new PhoneListCtrl(scope);
  });

  it('should create "phones" model with 3 phones', function() {
   expect(scope.phones.length).toBe(3);
  });

  it('should set the default value of orderProp model', function() {
   expect(scope.orderProp).toBe('age');
  });
 });
});

单元测试现在验证了默认值被正确设置。

我们使用Jasmine的接口把PhoneListCtrl控制器提取到一个beforeEach块中,这个块会被所有的父块describe中的所有测试所共享。

运行这些单元测试,跟以前一样,执行./scripts/test.sh脚本,你应该会看到如下输出(注意:要在浏览器打开http://localhost:9876并进入严格模式,测试才会运行!):

Chrome: Runner reset.
..
Total 2 tests (Passed: 2; Fails: 0; Errors: 0) (3.00 ms)
 Chrome 19.0.1084.36 Mac OS: Run 2 tests (Passed: 2; Fails: 0; Errors 0) (3.00 ms)

现在我们把注意力转移到端到端测试上来。

test/e2e/scenarios.js:

...
  it('should be possible to control phone order via the drop down select box',
    function() {
   //let's narrow the dataset to make the test assertions shorter
   input('query').enter('tablet');

   expect(repeater('.phones li', 'Phone List').column('phone.name')).
     toEqual(["Motorola XOOM\u2122 with Wi-Fi",
          "MOTOROLA XOOM\u2122"]);

   select('orderProp').option('Alphabetical');

   expect(repeater('.phones li', 'Phone List').column('phone.name')).
     toEqual(["MOTOROLA XOOM\u2122",
          "Motorola XOOM\u2122 with Wi-Fi"]);
  });
...

端到端测试验证了选项框的排序机制是正确的。

你现在可以刷新你的浏览器,然后重新跑一遍端到端测试,或者你可以在AngularJS的服务器上运行一下。

练习

在PhoneListCtrl控制器中,把设置orderProp那条语句删掉,你会看到AngularJS会在下拉菜单中临时添加一个空白的选项,并且排序顺序是默认排序(即未排序)。

在index.html模板里面添加一个`{{orderProp}}绑定来实时显示它的值。

总结

现在你已经为你的应用提供了搜索功能,并且完整的进行了测试。步骤5我们将学习AngularJS的服务以及AngularJS如何使用依赖注入。

以上就是对AngularJS 双向绑定的资料整理,后续继续补充相关资料,谢谢大家对本站的支持!

Javascript 相关文章推荐
ASP.NET jQuery 实例16 通过控件CustomValidator验证RadioButtonList
Feb 03 Javascript
html超链接打开窗口大小的方法
Mar 05 Javascript
浅析js中取绝对值的2种方法
Jul 09 Javascript
JS数组操作(数组增加、删除、翻转、转字符串、取索引、截取(切片)slice、剪接splice、数组合并)
May 20 Javascript
jQuery简单实现iframe的高度根据页面内容自适应的方法
Aug 01 Javascript
BootStrap入门教程(三)之响应式原理
Sep 19 Javascript
基于JS实现弹出一个隐藏的div窗口body页面变成灰色并且不可被编辑
Dec 14 Javascript
微信小程序 WebSocket详解及应用
Jan 21 Javascript
面包屑导航详解
Dec 07 Javascript
解决vue动态为数据添加新属性遇到的问题
Sep 18 Javascript
layui输入框中只允许输入整数的实现方法
Sep 18 Javascript
vue组件冲突之引用另一个组件出现组件不显示的问题
Apr 13 Vue.js
AngularJS入门教程之迭代器过滤详解
Aug 18 #Javascript
AngularJS入门教程之AngularJS 模板
Aug 18 #Javascript
AngularJS入门教程之静态模板详解
Aug 18 #Javascript
AngularJS入门教程引导程序
Aug 18 #Javascript
Bootstrap模态框(modal)垂直居中的实例代码
Aug 18 #Javascript
js 获取站点应用名的简单实例
Aug 18 #Javascript
BootStrap日期控件在模态框中选择时间下拉菜单无效的原因及解决办法(火狐下不能点击)
Aug 18 #Javascript
You might like
PHP字符串比较函数strcmp()和strcasecmp()使用总结
2014/11/19 PHP
THINKPHP内容分页代码分享
2015/01/14 PHP
PHP利用imagick生成组合缩略图
2016/02/19 PHP
详解PHP中array_rand函数的使用方法
2016/09/11 PHP
php+ajax实现异步上传文件或图片功能
2017/07/18 PHP
网页javascript精华代码集
2007/01/24 Javascript
JS 文字符串转换unicode编码函数
2009/05/30 Javascript
使用SyntaxHighlighter实现HTML高亮显示代码的方法
2010/02/04 Javascript
jQuery获取文本节点之 text()/val()/html() 方法区别
2011/03/01 Javascript
JavaScript字符串插入、删除、替换函数使用示例
2013/07/25 Javascript
javascript闭包传参和事件的循环绑定示例探讨
2014/04/17 Javascript
jquery实现界面无刷新加载登陆注册
2016/07/30 Javascript
js利用clipboardData实现截屏粘贴功能
2016/10/12 Javascript
基于JS实现弹出一个隐藏的div窗口body页面变成灰色并且不可被编辑
2016/12/14 Javascript
ES6 javascript的异步操作实例详解
2017/10/30 Javascript
vue使用代理解决请求跨域问题详解
2019/07/24 Javascript
解决包含在label标签下的checkbox在ie8及以下版本点击事件无效果兼容的问题
2019/10/27 Javascript
js实现QQ邮箱邮件拖拽删除功能
2020/08/27 Javascript
[00:43]FTP典藏礼包 DOTA2三大英雄霸气新套装
2014/03/21 DOTA
推荐11个实用Python库
2015/01/23 Python
python基础教程之分支、循环简单用法
2016/06/16 Python
python中如何使用朴素贝叶斯算法
2017/04/06 Python
Python 装饰器实现DRY(不重复代码)原则
2018/03/05 Python
python如何让类支持比较运算
2018/03/20 Python
Python中栈、队列与优先级队列的实现方法
2019/06/30 Python
利用Python实现kNN算法的代码
2019/08/16 Python
完美解决jupyter由于无法import新包的问题
2020/05/26 Python
Python实现淘宝秒杀功能的示例代码
2021/01/19 Python
纯CSS3实现8组超炫酷鼠标滑过图片动画
2016/03/16 HTML / CSS
股权转让协议书
2014/04/12 职场文书
护理专业求职信
2014/06/15 职场文书
大学生安全责任书
2014/07/25 职场文书
2015年医院工作总结范文
2015/04/09 职场文书
幼儿园教师师德师风承诺书
2015/04/28 职场文书
2019年自助餐厅创业计划书模板
2019/08/22 职场文书
python基础学习之生成器与文件系统知识总结
2021/05/25 Python