我們在上一步做了很多基礎性的訓練,所以現在我們可以來做一些簡單的事情嘍。我們要加入全文檢索功能(沒錯,這個真的非常簡單!)。同時,我們也會寫一個端到端測試,因為一個好的端到端測試可以幫上很大忙。它監視著你的應用,并且在發生回歸的時候迅速報告。
請重置工作目錄:
~~~
git checkout -f step-3
~~~
我們的應用現在有了一個搜索框。注意到頁面上的手機列表隨著用戶在搜索框中的輸入而變化。
步驟2和步驟3之間最重要的不同在下面列出。你可以在[GitHub](https://github.com/angular/angular-phonecat/compare/step-2...step-3)里看到完整的差別。
## 控制器
我們對控制器不做任何修改。
## 模板
app/index.html
~~~
<div class="container-fluid">
<div class="row-fluid">
<div class="span2">
<!--Sidebar content-->
Search: <input ng-model="query">
</div>
<div class="span10">
<!--Body content-->
<ul class="phones">
<li ng-repeat="phone in phones | filter:query">
{{phone.name}}
<p>{{phone.snippet}}</p>
</li>
</ul>
</div>
</div>
</div>
~~~
我們現在添加了一個`<input>`標簽,并且使用AngularJS的[$filter](http://docs.angularjs.org/api/ng.filter:filter)函數來處理[ngRepeat](http://docs.angularjs.org/api/ng.directive:ngRepeat)指令的輸入。
這樣允許用戶輸入一個搜索條件,立刻就能看到對電話列表的搜索結果。我們來解釋一下新的代碼:
* 數據綁定: 這是AngularJS的一個核心特性。當頁面加載的時候,AngularJS會根據輸入框的屬性值名字,將其與數據模型中相同名字的變量綁定在一起,以確保兩者的同步性。
在這段代碼中,用戶在輸入框中輸入的數據名字稱作`query`,會立刻作為列表迭代器(`phone in phones | filter:`query`)其過濾器的輸入。當數據模型引起迭代器輸入變化的時候,迭代器可以高效得更新DOM將數據模型最新的狀態反映出來。
* 使用`filter`過濾器:[filter](http://docs.angularjs.org/api/ng.filter:filter)函數使用`query`的值來創建一個只包含匹配`query`記錄的新數組。
`ngRepeat`會根據`filter`過濾器生成的手機記錄數據數組來自動更新視圖。整個過程對于開發者來說都是透明的。
## 測試
在步驟2,我們學習了編寫和運行一個測試的方法。單元測試用來測試我們用js編寫的控制器和其他組件都非常方便,但是不能方便的對DOM操作和應用集成進行測試。對于這些來說,端到端測試是一個更好的選擇。
搜索特性是完全通過模板和數據綁定實現的,所以我們的第一個端到端測試就來驗證這些特性是否符合我們的預期。
test/e2e/scenarios.js:
~~~
describe('PhoneCat App', function() {
describe('Phone list view', function() {
beforeEach(function() {
browser().navigateTo('../../app/index.html');
});
it('should filter the phone list as user types into the search box', function() {
expect(repeater('.phones li').count()).toBe(3);
input('query').enter('nexus');
expect(repeater('.phones li').count()).toBe(1);
input('query').enter('motorola');
expect(repeater('.phones li').count()).toBe(2);
});
});
});
~~~
盡管這段測試代碼的語法看起來和我們之前用Jasmine寫的單元測試非常像,但是端到端測試使用的是[AngularJS端到端測試器](http://code.angularjs.org/1.1.0/docs/guide/dev_guide.e2e-testing)提供的接口。
運行一個端到端測試,在瀏覽器新標簽頁中打開下面任意一個:
* node.js用戶:[http://localhost:8000/test/e2e/runner.html](http://localhost:8000/test/e2e/runner.html)
* 使用其他http服務器的用戶:`http://localhost:[port-number]/[context-path]/test/e2e/runner.html`
* 訪客:[http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html](http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html)
這個測試驗證了搜素框和迭代器被正確地集成起來。你可以發現,在AngularJS里寫一個端到端測試多么的簡單。盡管這個例子僅僅是一個簡單的測試,但是用它來構建任何一個復雜、可讀的端到端測試都很容易。
## 練習
* 在`index.html`模板中添加一個`{{query}}`綁定來實時顯示`query`模型的當前值,然后觀察他們是如何根據輸入框中的值而變化。
* 現在我們來看一下我們怎么讓`query`模型的值出現在HTML的頁面標題上。
你或許認為像下面這樣在`title`標簽上加上一個綁定就行了:
~~~
<title>Google Phone Gallery: {{query}}</title>
~~~
但是,當你重載頁面的時候,你根本沒辦法得到期望的結果。這是因為`query`模型僅僅在`body`元素定義的作用域內才有效。
~~~
<body ng-controller="PhoneListCtrl">
~~~
如果你想讓`<title>`元素綁定上`query`模型,你必須把`ngController`聲明移動到`HTML`元素上,因為它是`title`和`body`元素的共同祖先。
~~~
<html ng-app ng-controller="PhoneListCtrl">
~~~
一定要注意把`body`元素上的`ng-controller`聲明給刪了。
當綁定兩個花括號在`title`元素上可以實現我們的目標,但是你或許發現了,頁面正加載的時候它們已經顯示給用戶看了。一個更好的解決方案是使用[ngBind](http://docs.angularjs.org/api/ng.directive:ngBind)或者[ngBindTemplate](http://docs.angularjs.org/api/ng.directive:ngBindTemplate)指令,它們在頁面加載時對用戶是不可見的:
~~~
<title ng-bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
~~~
* 在`test/e2e/scenarios.js`的`describe`塊中加入下面這些端到端測試代碼:
~~~
it('should display the current filter value within an element with id "status"',
function() {
expect(element('#status').text()).toMatch(/Current filter: \s*$/);
input('query').enter('nexus');
expect(element('#status').text()).toMatch(/Current filter: nexus\s*$/);
//alternative version of the last assertion that tests just the value of the binding
using('#status').expect(binding('query')).toBe('nexus');
});
~~~
刷新瀏覽器,端到端測試器會報告測試失敗。為了讓測試通過,編輯`index.html`,添加一個`id為“status”`的`div`或者`p`元素,內容是一個`query`綁定,再加上`Current filter:`前綴。例如:
~~~
<div id="status">Current filter: {{query}}</div>
~~~
* 在端到端測試里面加一條`pause();`語句,重新跑一遍。你將發現測試器暫停了!這樣允許你有機會在測試運行過程中查看你應用的狀態。測試應用是實時的!你可以更換搜索內容來證明。稍有經驗你就會知道,這對于在端到端測試中迅速找到問題是多么的關鍵。
## 總結
我們現在添加了全文搜索功能,并且完成一個測試證明了搜索是對的!現在讓我們繼續到[步驟4](a007)來看看給我們的手機應用增加排序功能。