一起学AngularJS

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

2016-03-10  本文已影响127人  40ab6525bf35

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

这个例子中,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';
});

上述代码主要包含以下改动:

这个例子能非常好的说明双向绑定这个概念。当页面载入后,我们可以看到下拉框显示的是"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的服务体系和依赖注入。

上一篇 下一篇

猜你喜欢

热点阅读