【一起学AngularJS】第六章、双向数据绑定

本章节中,我们将为之前展示的手机信息列表提供一个排序功能。要想实现动态排序的功能,我们只需要新建一个模型属性,并且把它和循环器绑定在一起,剩下的就交给数据绑定去完成了。
相比上一章中的例子,本章中的例子多添加了一个下拉菜单,它允许用户自己控制手机列表的显示顺序。首先我们把代码切换到step-4:

git checkout -f step-4

刷新浏览器查看效果(假设之前你已经运行了上一章的例子)。你可以在线看效果

模版

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">
    <span>{{phone.name}}</span>
    <p>{{phone.snippet}}</p>
  </li>
</ul>

代码对index.html主要做了以下几点改动:
* 第一布我们添加一个名为orderProp<select>标签元素,这样用户就能从提供的两种排序中选择自己想要的排序方式了。

* 第二步我们再之前的filter过滤器链中添加一个orderBy过滤器用来对结果进行排序。orderBy过滤器的输入为一个数组,对其进行排序并且返回排序后的数组。

这个例子中,Angular在<select>元素和orderProp数据模型之间建立双向数据绑定,然后orderBy过滤器就可以用它作为输入,并且返回结果了。
上一章中我们讨论了关于循环器的数据绑定,只要模型数据发生变化(比如用户下拉排序菜单选择了不通的排序方式),Angular的数据绑定功能将确保页面会同步展示最新结果。其他的手工控制代码都不用自己写。

控制器

app/js/controllers.js:

var phonecatApp = angular.module('phonecatApp', []);

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

  $scope.orderProp = 'age';
});

上述代码主要包含以下改动:
* 首先我们修改了phones数据模型,为每条手机记录添加了一个age属性。页面将支持根据这个属性来排序。
* 另外我们在接下来的一行中把orderProp的默认值设成age(告诉页面第一次载入时按照age排序)。如果这里没有设置默认值,页面在载入的时候将是未排序过的,直到用户从下拉菜单中选择了一个排序方式。

这个例子能非常好的说明双向绑定这个概念。当页面载入后,我们可以看到下拉框显示的是”Newest”,这是因为我们把orderProp默认设置为了age。这是一次从数据模型到UI的单向绑定。另外一方面,当我们选择下拉菜单的”Alphabetical”,后台中的手机数组列表就会被重新排列,这就是从UI到数据模型的单向绑定。这两者结合起来,就是数据的双向绑定了。

测试

这次的改动将涉及单元测试和E2E测试两个测试。我们先看以下单元测试:
test/unit/controllersSpec.js:

describe('PhoneCat controllers', function() {

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

    beforeEach(module('phonecatApp'));

    beforeEach(inject(function($controller) {
      scope = {};
      ctrl = $controller('PhoneListCtrl', {$scope: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');
    });
  });
});

上述单元测试代码将验证orderProp默认值是否设置成功。
我们使用了Jasmine的API来把控制器的构造过程抽取出来放入了beforeEach模块中,这将使得它能被父模块describe中的所有测试所共享。
你将会从Karma的控制台输出中看到以下消息:

Chrome 22.0: Executed 2 of 2 SUCCESS (0.021 secs / 0.001 secs)

下面让我们来看看E2E测试,以下是代码:
test/e2e/scenarios.js:

...
it('should be possible to control phone order via the drop down select box', function() {

  var phoneNameColumn = element.all(by.repeater('phone in phones').column('phone.name'));
  var query = element(by.model('query'));

  function getNames() {
    return phoneNameColumn.map(function(elm) {
      return elm.getText();
    });
  }

  query.sendKeys('tablet'); //let's narrow the dataset to make the test assertions shorter

  expect(getNames()).toEqual([
    "Motorola XOOM\u2122 with Wi-Fi",
    "MOTOROLA XOOM\u2122"
  ]);

  element(by.model('orderProp')).element(by.css('option[value="name"]')).click();

  expect(getNames()).toEqual([
    "MOTOROLA XOOM\u2122",
    "Motorola XOOM\u2122 with Wi-Fi"
  ]);
});...

E2E测试将用来验证下拉选择框的排序功能是否正常。你可以通过npm run protractor来运行这个测试用例。

实验小能手

  1. PhoneListCtrl控制器代码中,把$scope.orderProp = 'age';这句话注释掉,然后你就会看到Angular会为页面上的下拉框添加了一个新的空白选项(Unknown),并且当前的列表也是未经排序的。
  2. 添加一个{{orderProp}}数据绑定用来查看当前的排序方式。
  3. 想要倒排某个排序?很简单,尝试添加一个新的select选项:<option value="-age">Oldest</option>

总结

现在我们已经为网站添加了排序功能,下一章,我们将学习Angular的服务体系和依赖注入。

博客原文: http://www.tbwood.cn/articles/2016/03/10/1457591930686.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值