我們?cè)谏弦徊街袨殚_發(fā)應(yīng)用打基礎(chǔ)做了很多工作,現(xiàn)在我們將做一些簡單的事情;我們將添加全文搜索(是的,它很簡單!)。我們還將編寫一個(gè)端到端測(cè)試,因?yàn)橐粋€(gè)好的端到端測(cè)試可以幫上大忙。它監(jiān)視著你的應(yīng)用,并在發(fā)生回歸時(shí)迅速報(bào)告。
把工作空間重置到第三步
git checkout -f step-3
刷新你的瀏覽器或在線檢查這一步:Step 3 Live Demo
下面列出了第二步和第三步之間最重要的區(qū)別。你可以在GitHub里看到完整的差異。
我們對(duì)控制器不作修改。
app/index.html:
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<!--Sidebar content-->
Search: <input ng-model="query">
</div>
<div class="col-md-10">
<!--Body content-->
<ul class="phones">
<li ng-repeat="phone in phones | filter:query">
{{phone.name}}
<p>{{phone.snippet}}</p>
</li>
</ul>
</div>
</div>
</div>
我們添加了一個(gè)標(biāo)準(zhǔn)HTML<input>
元素標(biāo)記,并使用Angular的filter函數(shù)來處理repeat指令的輸入。
這使用戶輸入搜索條件,并在手機(jī)列表中快速看到搜索結(jié)果。新的代碼演示如下:
數(shù)據(jù)綁定:這是Angular的一個(gè)核心功能。當(dāng)網(wǎng)頁載入時(shí),Angular把輸入框的名稱綁定到數(shù)據(jù)模塊的同名的變量上,并保持兩者同步。
在代碼中,用戶打字到輸入框的數(shù)據(jù)(命名為query
)很快可以作為一個(gè)篩選器輸入到列表迭代器(phone in phones | filter:
query
)中。在改變數(shù)據(jù)模塊的時(shí)候,導(dǎo)致迭代器的輸入發(fā)生變化,迭代器有效地更新了DOM,以反映模塊的當(dāng)前狀態(tài)。
使用filter
篩選器:filter函數(shù)使用了query
值發(fā)創(chuàng)建一個(gè)新的數(shù)列,只包含匹配query
的記錄。
ngRepeat
自動(dòng)更新了視力,以響應(yīng)filter
篩選器返回的手機(jī)數(shù)字的變化。該處理對(duì)開發(fā)者來說是完全透明的。
在第二步中,我們學(xué)會(huì)了如何編寫并運(yùn)行單元測(cè)試。對(duì)于測(cè)試我們的用JavaScript編寫的應(yīng)用程序的控制器和其它組件,單元測(cè)試是完美的,但是測(cè)試DOM操作或測(cè)試我們的應(yīng)用程序的接通不太方便。針對(duì)這些,一個(gè)端到端的測(cè)試是一個(gè)更好的選擇。
該搜索功能完全是通過模板和數(shù)據(jù)綁定來實(shí)現(xiàn)的,我們將編寫我們第一個(gè)端到端的測(cè)試,以驗(yàn)證該功能起了什么作用。
test/e2e/scenarios.js
:
describe('PhoneCat App', function() {
describe('Phone list view', function() {
beforeEach(function() {
browser.get('app/index.html');
});
it('should filter the phone list as a user types into the search box', function() {
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
expect(phoneList.count()).toBe(3);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(2);
});
});
});
這個(gè)測(cè)試驗(yàn)證了搜索框以及迭代器是否正確地接通了。注意,在Angular中,編寫端到端測(cè)試是如此地容易。雖然這個(gè)示例只針對(duì)一個(gè)簡單的測(cè)試,但是它確實(shí)很容易測(cè)試任何功能化的、可讀的、端到端的測(cè)試。
甚至雖然測(cè)試的句法看起來很像我們的用Jasmine編寫的控制器單元測(cè)試,但是端到端測(cè)試使用Protractor的API。在http://angular.github.io/protractor/#/api可以讀到Protractor的API。
與Karma很像的是針對(duì)單元測(cè)試的測(cè)試運(yùn)行者,我們使用Protractor以運(yùn)行端到端測(cè)試。用npm run protractor
來嘗試它。端到端測(cè)試很慢,所以與單元測(cè)試不同,在運(yùn)行測(cè)試之后Protractor將退出,不會(huì)自動(dòng)在每次文件更改時(shí)重新運(yùn)行測(cè)試套裝。要想重新運(yùn)行測(cè)試套裝,需要再次執(zhí)行npm run protractor
。
通過添加一個(gè)綁定到index.html
模板的{{query}}
來顯示query
模塊當(dāng)前的值,并看到當(dāng)你在輸入框中打字時(shí),它如何變化。
讓我們看到我們可以取得query
模板的當(dāng)前值,模塊出現(xiàn)在HTML網(wǎng)頁的標(biāo)題上。
把一個(gè)端到端測(cè)試添加到describe
塊中,test/e2e/scenarios.js
看起來將如這:
describe('PhoneCat App', function() {
describe('Phone list view', function() {
beforeEach(function() {
browser.get('app/index.html');
});
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
it('should filter the phone list as a user types into the search box', function() {
expect(phoneList.count()).toBe(3);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(2);
});
it('should display the current filter value in the title bar', function() {
query.clear();
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
query.sendKeys('nexus');
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
});
});
});
運(yùn)行protractor(npm run protractor
),看到測(cè)試失敗了。
你可能認(rèn)為你只需要用以下方式向標(biāo)題標(biāo)簽添加{{query}}
:
<title>Google Phone Gallery: {{query}}</title>
然而,當(dāng)你重載入這個(gè)網(wǎng)頁的時(shí)候,你不會(huì)看到想要的結(jié)果。這是因?yàn)椤安樵儭蹦K駐留在作用域內(nèi),由ng-controller="PhoneListCtrl"
指令在body元素上定義。
<body ng-controller="PhoneListCtrl">
如果你想要從<title>
元素上綁定查詢模塊,你必須把ngController
聲明移動(dòng)到HTML元素上,因?yàn)樗莃ody元素和title元素常用的父元素。
<html ng-app="phonecatApp" ng-controller="PhoneListCtrl">
確保從body元素中移除ng-controller
聲明。
重新運(yùn)行rpm run protractor
,看到現(xiàn)在測(cè)試已經(jīng)看通過了。
在title元素內(nèi)部使用雙花工作得很好,與此同時(shí),你可能會(huì)注意到頁面加載的一瞬間它們確實(shí)顯示給用戶了。一個(gè)更好的解決方案是使用ngBind指令或ngBindTemplate指令,當(dāng)頁面加載時(shí)用戶能看到它們。
<title ng-bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
我們現(xiàn)在已經(jīng)把全文搜索添加上去了,還包含了一個(gè)用來驗(yàn)證搜索是否起作用的測(cè)試!現(xiàn)在讓我們前往第四步 雙路數(shù)據(jù)綁定以學(xué)會(huì)如何向手機(jī)應(yīng)用添加排序功能。
更多建議: