鍍金池/ 教程/ HTML/ 雙向綁定 - Two-way Data Binding
完結(jié)篇 - The End
迭代器 - Filtering Repeaters
過濾器 - Filters
靜態(tài)模版 - Static Template
引導(dǎo)程序 - Bootstrapping
路由與多視圖 - Routing Multiple Views
動畫操作 - Applying Animations
導(dǎo)言
雙向綁定 - Two-way Data Binding
更多模版 - More Templating
連接與圖片模版- Templating Links Images
事件處理器 - Event Handlers
AngularJS 模版 - Angular Templates
XHR 和依賴注入 - XHRs Dependency Injection
REST 和定制服務(wù) - REST and Custom Services

雙向綁定 - Two-way Data Binding

在這一步你會增加一個讓用戶控制手機列表顯示順序的特性。動態(tài)排序可以這樣實現(xiàn),添加一個新的模型屬性,把它和迭代器集成起來,然后讓數(shù)據(jù)綁定完成剩下的事情。

請重置工作目錄:

    git checkout -f step-4

你應(yīng)該發(fā)現(xiàn)除了搜索框之外,你的應(yīng)用多了一個下來菜單,它可以允許控制電話排列的順序。

步驟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>標(biāo)簽,這樣我們的用戶就可以選擇我們提供的兩種排序方法。

http://wiki.jikexueyuan.com/project/angularjs-tutorial/images/two-way-data-binding-1.png" alt="Image of two-way-data-binding-1.png" />

  • 然后,在filter過濾器后面添加一個orderBy過濾器用其來處理進入迭代器的數(shù)據(jù)。orderBy過濾器以一個數(shù)組作為輸入,復(fù)制一份副本,然后把副本重排序再輸出到迭代器。

AngularJS在select元素和orderProp模型之間創(chuàng)建了一個雙向綁定。而后,orderProp會被用作orderBy過濾器的輸入。

正如我們在步驟3中討論數(shù)據(jù)綁定和迭代器的時候所說的一樣,無論什么時候數(shù)據(jù)模型發(fā)生了改變(比如用戶在下拉菜單中選了不同的順序),AngularJS的數(shù)據(jù)綁定會讓視圖自動更新。沒有任何笨拙的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模型—— 手機的數(shù)組 ——為每一個手機記錄其增加了一個age屬性。我們會根據(jù)age屬性來對手機進行排序。
  • 我們在控制器代碼里加了一行讓orderProp的默認(rèn)值為age。如果我們不設(shè)置默認(rèn)值,這個模型會在我們的用戶在下拉菜單選擇一個順序之前一直處于未初始化狀態(tài)。

    現(xiàn)在我們該好好談?wù)勲p向數(shù)據(jù)綁定了。注意到當(dāng)應(yīng)用在瀏覽器中加載時,“Newest”在下拉菜單中被選中。這是因為我們在控制器中把orderProp設(shè)置成了‘a(chǎn)ge’。所以綁定在從我們模型到用戶界面的方向上起作用——即數(shù)據(jù)從模型到視圖的綁定?,F(xiàn)在當(dāng)你在下拉菜單中選擇“Alphabetically”,數(shù)據(jù)模型會被同時更新,并且手機列表數(shù)組會被重新排序。這個時候數(shù)據(jù)綁定從另一個方向產(chǎn)生了作用——即數(shù)據(jù)從視圖到模型的綁定。

測試

我們所做的更改可以通過一個單元測試或者一個端到端測試來驗證正確性。我們首先來看看單元測試:

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');
        });
      });
    });

單元測試現(xiàn)在驗證了默認(rèn)值被正確設(shè)置。

我們使用Jasmine的接口把PhoneListCtrl控制器提取到一個beforeEach塊中,這個塊會被所有的父塊describe中的所有測試所共享。

運行這些單元測試,跟以前一樣,執(zhí)行./scripts/test.sh腳本,你應(yīng)該會看到如下輸出(注意:要在瀏覽器打開http://localhost:9876并進入嚴(yán)格模式,測試才會運行?。?/p>

    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)

現(xiàn)在我們把注意力轉(zhuǎn)移到端到端測試上來。

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"]);
        });
    ...

端到端測試驗證了選項框的排序機制是正確的。

你現(xiàn)在可以刷新你的瀏覽器,然后重新跑一遍端到端測試。

練習(xí)

  • PhoneListCtrl控制器中,把設(shè)置orderProp那條語句刪掉,你會看到AngularJS會在下拉菜單中臨時添加一個空白的選項,并且排序順序是默認(rèn)排序(即未排序)。
  • index.html模板里面添加一個`{{orderProp}}綁定來實時顯示它的值。

總結(jié)

現(xiàn)在你已經(jīng)為你的應(yīng)用提供了搜索功能,并且完整的進行了測試。XHR和依賴注入我們將學(xué)習(xí)AngularJS的服務(wù)以及AngularJS如何使用依賴注入。