AngularJS入門教程(使用超動感HTML和JS設(shè)計WEB應(yīng)用)_第1頁
AngularJS入門教程(使用超動感HTML和JS設(shè)計WEB應(yīng)用)_第2頁
AngularJS入門教程(使用超動感HTML和JS設(shè)計WEB應(yīng)用)_第3頁
AngularJS入門教程(使用超動感HTML和JS設(shè)計WEB應(yīng)用)_第4頁
AngularJS入門教程(使用超動感HTML和JS設(shè)計WEB應(yīng)用)_第5頁
已閱讀5頁,還剩67頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

AngularJS入門教程使用超動感HTML和JS設(shè)計WEB應(yīng)用目錄\hAngularJS入門教程目錄\hAngularJS快速開始\hAngularJS入門教程:導(dǎo)言和準備\hAngularJS入門教程00:引導(dǎo)程序\hAngularJS入門教程01:靜態(tài)模板\hAngularJS入門教程02:AngularJS模板\hAngularJS入門教程03:迭代器過濾\hAngularJS入門教程04:雙向綁定\hAngularJS入門教程05:XHR和依賴注入\hAngularJS入門教程06:鏈接與圖片模板\hAngularJS入門教程07:路由與多視圖\hAngularJS入門教程08:更多模板\hAngularJS入門教程09:過濾器\hAngularJS入門教程10:事件處理器\hAngularJS入門教程11:REST和定制服務(wù)\hAngularJS入門教程:完結(jié)篇

AngularJS入門教程目錄\h快速開始\h導(dǎo)言和準備\h引導(dǎo)程序\h靜態(tài)模板\hAngularJS模板\h迭代器過濾\h雙向綁定\hXHR和依賴注入\h鏈接與圖片模板\h路由與多視圖\h更多模板\h過濾器\h事件處理器\hREST和定制服務(wù)\h完結(jié)篇

AngularJS快速開始HelloWorld!開始學(xué)習(xí)AngularJS的一個好方法是創(chuàng)建經(jīng)典應(yīng)用程序“HelloWorld!”:使用您喜愛的文本編輯器,創(chuàng)建一個HTML文件,例如:helloworld.html。將下面的源代碼復(fù)制到您的HTML文件。在web瀏覽器中打開這個HTML文件。源代碼<!doctypehtml>

<htmlng-app>

<head>

<scriptsrc="/angular-1.0.1.min.js"></script>

</head>

<body>

Hello{{'World'}}!

</body>

</html>

請在您的瀏覽器中運行以上代碼查看效果?,F(xiàn)在讓我們仔細看看代碼,看看到底怎么回事。當加載該頁時,標記ng-app告訴AngularJS處理整個HTML頁并引導(dǎo)應(yīng)用:<htmlng-app>

這行載入AngularJS腳本:<scriptsrc="/angular-1.0.1.min.js"></script>

(想了解AngularJS處理整個HTML頁的細節(jié),請看Bootstrap。)最后,標簽中的正文是應(yīng)用的模板,在UI中顯示我們的問候語:Hello{{'World'}}!

注意,使用雙大括號標記{{}}的內(nèi)容是問候語中綁定的表達式,這個表達式是一個簡單的字符串‘World’。下面,讓我們看一個更有趣的例子:使用AngularJS對我們的問候語文本綁定一個動態(tài)表達式。HelloAngularJSWorld!本示例演示AngularJS的雙向數(shù)據(jù)綁定(bi-directionaldatabinding):編輯前面創(chuàng)建的helloworld.html文檔。將下面的源代碼復(fù)制到您的HTML文件。刷新瀏覽器窗口。源代碼<!doctypehtml>

<htmlng-app>

<head>

<scriptsrc="/angular-1.0.1.min.js"></script>

</head>

<body>

Yourname:<inputtype="text"ng-model="yourname"placeholder="World">

<hr>

Hello{{yourname||'World'}}!

</body>

</html>

請在您的瀏覽器中運行以上代碼查看效果。該示例有一下幾點重要的注意事項:文本輸入指令<inputng-model="yourname"/>綁定到一個叫yourname的模型變量。雙大括號標記將yourname模型變量添加到問候語文本。你不需要為該應(yīng)用另外注冊一個事件偵聽器或添加事件處理程序!現(xiàn)在試著在輸入框中鍵入您的名稱,您鍵入的名稱將立即更新顯示在問候語中。這就是AngularJS雙向數(shù)據(jù)綁定的概念。輸入框的任何更改會立即反映到模型變量(一個方向),模型變量的任何更改都會立即反映到問候語文本中(另一方向)。AngularJS應(yīng)用的解析本節(jié)描述AngularJS應(yīng)用程序的三個組成部分,并解釋它們?nèi)绾斡成涞侥P?視圖-控制器設(shè)計模式:模板(Templates)模板是您用HTML和CSS編寫的文件,展現(xiàn)應(yīng)用的視圖。您可給HTML添加新的元素、屬性標記,作為AngularJS編譯器的指令。AngularJS編譯器是完全可擴展的,這意味著通過AngularJS您可以在HTML中構(gòu)建您自己的HTML標記!應(yīng)用程序邏輯(Logic)和行為(Behavior)應(yīng)用程序邏輯和行為是您用JavaScript定義的控制器。AngularJS與標準AJAX應(yīng)用程序不同,您不需要另外編寫偵聽器或DOM控制器,因為它們已經(jīng)內(nèi)置到AngularJS中了。這些功能使您的應(yīng)用程序邏輯很容易編寫、測試、維護和理解。模型數(shù)據(jù)(Data)模型是從AngularJS作用域?qū)ο蟮膶傩砸甑?。模型中的?shù)據(jù)可能是Javascript對象、數(shù)組或基本類型,這都不重要,重要的是,他們都屬于AngularJS作用域?qū)ο?。AngularJS通過作用域來保持數(shù)據(jù)模型與視圖界面UI的雙向同步。一旦模型狀態(tài)發(fā)生改變,AngularJS會立即刷新反映在視圖界面中,反之亦然。此外,AngularJS還提供了一些非常有用的服務(wù)特性:底層服務(wù)包括依賴注入,XHR、緩存、URL路由和瀏覽器抽象服務(wù)。您還可以擴展和添加自己特定的應(yīng)用服務(wù)。這些服務(wù)可以讓您非常方便的編寫WEB應(yīng)用。

AngularJS入門教程:導(dǎo)言和準備學(xué)習(xí)AngularJS的一個好方法是逐步完成本教程,它將引導(dǎo)您構(gòu)建一個完整的AngularJSweb應(yīng)用程序。該web應(yīng)用是一個Android設(shè)備清單的目錄列表,您可以篩選列表以便查看您感興趣的設(shè)備,然后查看設(shè)備的詳細信息。本教程將向您展示AngularJS怎樣使得web應(yīng)用更智能更靈活,而且不需要各種擴展程序或插件。通過本教程的學(xué)習(xí),您將:閱讀示例學(xué)習(xí)怎樣使用AngularJS的客戶端數(shù)據(jù)綁定和依賴注入功能來建立可立即響應(yīng)用戶操作的動態(tài)數(shù)據(jù)視圖。學(xué)習(xí)如何使用AngularJS創(chuàng)建數(shù)據(jù)偵聽器,且不需要進行DOM操作。學(xué)習(xí)一種更好、更簡單的方法來測試您的web應(yīng)用程序。學(xué)習(xí)如何使用AngularJS創(chuàng)建常見的web任務(wù),例如更方便的將數(shù)據(jù)引入應(yīng)用程序。而且這一切可在任何一個瀏覽器實現(xiàn),無需配置瀏覽器!當你完成了本教程后,您將學(xué)會:創(chuàng)建一個可在任何瀏覽器中的工作的動態(tài)應(yīng)用。了解AngularJS與其它JavaScript框架之間的區(qū)別。了解AngularJS如何實現(xiàn)數(shù)據(jù)綁定。利用AngularJS的種子項目快速創(chuàng)建自己的項目。創(chuàng)建和運行測試。學(xué)習(xí)更多AngularJS標識資源(API)。本教程將指導(dǎo)您完成一個簡單的應(yīng)用程序創(chuàng)建過程,包括編寫和運行單元測試、不斷地測試應(yīng)用。教程的每個步驟為您提供建議以了解更多有關(guān)AngularJS和您創(chuàng)建的web應(yīng)用程序。您可能會在短時間內(nèi)快速讀完本教程,也可能需要花大量時間深入研究本教程。如果想看一個簡短的AngularJS介紹文檔,請查看[快速開始][GettingStarted]文檔。搭建學(xué)習(xí)環(huán)境無論是Mac、Linux或Windows環(huán)境中,您均可遵循本教程學(xué)習(xí)編程。您可以使用源代碼管理版本控制系統(tǒng)Git獲取本教程項目的源代碼文件,或直接從網(wǎng)上下載本教程項目源代碼文件的鏡像歸檔壓縮包。您需要安裝Node.js和Testacular來運行本項目,請到\hNode.js官方網(wǎng)站下載并安裝最新版,然后把node可執(zhí)行程序路徑添加到系統(tǒng)環(huán)境變量PATH中,完成后在命令行中運行一下命令可以查看是否安裝成功:node-version

然后安裝\hTestacular單元測試程序,請運行如下命令:npminstall-gtestacular

安裝\hGit工具,然后用以下命令從Github復(fù)制本教程項目的源代碼文件:gitclonegit:///angular/angular-phonecat.git

您也可以直接從網(wǎng)上\h下載本教程項目源代碼的鏡像歸檔壓縮包。這個命令會在您當前文件夾中建立新文件夾angular-phonecat。最后一件事要做的就是確保您的計算機安裝了web瀏覽器和文本編輯器。進入教程源代碼文件包angular-phonecat,運行服務(wù)器后臺程序,開始學(xué)習(xí)AngularJS!cdangular-phonecat

nodescripts/web-server.js

AngularJS入門教程00:引導(dǎo)程序我們現(xiàn)在開始準備編寫AngularJS應(yīng)用——phonecat。這一步驟(步驟0),您將會熟悉重要的源代碼文件,學(xué)習(xí)啟動包含AngularJS種子項目的開發(fā)環(huán)境,并在瀏覽器端運行應(yīng)用。進入angular-phonecat目錄,運行如下命令:gitcheckout-fstep-0

該命令將重置phonecat項目的工作目錄,建議您在每一學(xué)習(xí)步驟運行此命令,將命令中的數(shù)字改成您學(xué)習(xí)步驟對應(yīng)的數(shù)字,該命令將清除您在工作目錄內(nèi)做的任何更改。運行以下命令:nodescripts/web-server.js

來啟動服務(wù)器,啟動后命令行終端將會提示HttpServerrunningathttp://localhost:8000,請不要關(guān)閉該終端,關(guān)閉該終端即關(guān)閉了服務(wù)器。在瀏覽器中輸入\hhttp://localhost:8000/app/index.html來訪問我們的phonecat應(yīng)用?,F(xiàn)在,在瀏覽器中您應(yīng)該已經(jīng)看到了我們的初始應(yīng)用,很簡單,但說明我們的項目已經(jīng)可以運行了。應(yīng)用中顯示的“Nothinghereyet!”是由如下HTML代碼構(gòu)建而成,代碼中包含了AngularJS的關(guān)鍵元素,正是我們需要學(xué)習(xí)的。app/index.html<!doctypehtml>

<htmllang="en"ng-app>

<head>

<metacharset="utf-8">

<title>MyHTMLFile</title>

<linkrel="stylesheet"href="css/app.css">

<linkrel="stylesheet"href="css/bootstrap.css">

<scriptsrc="lib/angular/angular.js"></script>

</head>

<body>

<p>Nothinghere{{'yet'+'!'}}</p>

</body>

</html>

代碼在做什么呢?ng-app指令:<htmllang="en"ng-app>

ng-app指令標記了AngularJS腳本的作用域,在<html>中添加ng-app屬性即說明整個<html>都是AngularJS腳本作用域。開發(fā)者也可以在局部使用ng-app指令,如<divng-app>,則AngularJS腳本僅在該<div>中運行。AngularJS腳本標簽:<scriptsrc="lib/angular/angular.js"></script>

這行代碼載入angular.js腳本,當瀏覽器將整個HTML頁面載入完畢后將會執(zhí)行該angular.js腳本,angular.js腳本運行后將會尋找含有ng-app指令的HTML標簽,該標簽即定義了AngularJS應(yīng)用的作用域。雙大括號綁定的表達式:<p>Nothinghere{{'yet'+'!'}}</p>

這行代碼演示了AngularJS模板的核心功能——綁定,這個綁定由雙大括號{{}}和表達式'yet'+'!'組成。這個綁定告訴AngularJS需要運算其中的表達式并將結(jié)果插入DOM中,接下來的步驟我們將看到,DOM可以隨著表達式運算結(jié)果的改變而實時更新。AngularJS表達式\hAngularexpression是一種類似于JavaScript的代碼片段,AngularJS表達式僅在AngularJS的作用域中運行,而不是在整個DOM中運行。引導(dǎo)AngularJS應(yīng)用通過ngApp指令來自動引導(dǎo)AngularJS應(yīng)用是一種簡潔的方式,適合大多數(shù)情況。在高級開發(fā)中,例如使用腳本裝載應(yīng)用,您也可以使用\hbootstrap手動引導(dǎo)AngularJS應(yīng)用。AngularJS應(yīng)用引導(dǎo)過程有3個重要點:\h注入器(injector)將用于創(chuàng)建此應(yīng)用程序的依賴注入(dependencyinjection);注入器將會創(chuàng)建根作用域作為我們應(yīng)用模型的范圍;AngularJS將會鏈接根作用域中的DOM,從用ngApp標記的HTML標簽開始,逐步處理DOM中指令和綁定。一旦AngularJS應(yīng)用引導(dǎo)完畢,它將繼續(xù)偵聽瀏覽器的HTML觸發(fā)事件,如鼠標點擊事件、按鍵事件、HTTP傳入響應(yīng)等改變DOM模型的事件。這類事件一旦發(fā)生,AngularJS將會自動檢測變化,并作出相應(yīng)的處理及更新。上面這個應(yīng)用的結(jié)構(gòu)非常簡單。該模板包僅含一個指令和一個靜態(tài)綁定,其中的模型也是空的。下一步我們嘗試稍復(fù)雜的應(yīng)用!我工作目錄中這些文件是干什么的?上面的應(yīng)用來自于AngularJS種子項目,我們通常可以使用\hAngularJS種子項目來創(chuàng)建新項目。種子項目包括最新的AngularJS代碼庫、測試庫、腳本和一個簡單的應(yīng)用程序示例,它包含了開發(fā)一個典型的web應(yīng)用程序所需的基本配置。對于本教程,我們對AngularJS種子項目進行了下列更改:刪除示例應(yīng)用程序;添加手機圖像到app/img/phones/;添加手機數(shù)據(jù)文件(JSON)到app/phones/;添加\hTwitterBootstrap文件到app/css/和app/img/。練習(xí)試試把關(guān)于數(shù)學(xué)運算的新表達式添加到index.html:<p>1+2={{1+2}}</p>

總結(jié)現(xiàn)在讓我們轉(zhuǎn)到\h步驟1,將一些內(nèi)容添加到web應(yīng)用程序。

AngularJS入門教程01:靜態(tài)模板為了說明angularJS如何增強了標準HTML,我們先將創(chuàng)建一個靜態(tài)HTML頁面模板,然后把這個靜態(tài)HTML頁面模板轉(zhuǎn)換成能動態(tài)顯示的AngularJS模板。在本步驟中,我們往HTML頁面中添加兩個手機的基本信息,用以下命令將工作目錄重置到步驟1。gitcheckout-fstep-1

請編輯app/index.html文件,將下面的代碼添加到index.html文件中,然后運行該應(yīng)用查看效果。app/index.html<ul>

<li>

<span>NexusS</span>

<p>

FastjustgotfasterwithNexusS.

</p>

</li>

<li>

<span>MotorolaXOOM?withWi-Fi</span>

<p>

TheNext,NextGenerationtablet.

</p>

</li>

</ul>

練習(xí)嘗試添加多個靜態(tài)HTML代碼到index.html,例如:<p>Totalnumberofphones:2</p>

總結(jié)本步驟往應(yīng)用中添加了靜態(tài)HTML手機列表,現(xiàn)在讓我們轉(zhuǎn)到\h步驟2以了解如何使用AngularJS動態(tài)生成相同的列表。

AngularJS入門教程02:AngularJS模板是時候給這些網(wǎng)頁來點動態(tài)特性了——用AngularJS!我們這里為后面要加入的控制器添加了一個測試。一個應(yīng)用的代碼架構(gòu)有很多種。對于AngularJS應(yīng)用,我們鼓勵使用\h模型-視圖-控制器(MVC)模式解耦代碼和分離關(guān)注點。考慮到這一點,我們用AngularJS來為我們的應(yīng)用添加一些模型、視圖和控制器。請重置工作目錄:gitcheckout-fstep-2

我們的應(yīng)用現(xiàn)在有了一個包含三部手機的列表。步驟1和步驟2之間最重要的不同在下面列出。,你可以到\hGitHub去看完整的差別。視圖和模板在AngularJS中,一個視圖是模型通過HTML**模板**渲染之后的映射。這意味著,不論模型什么時候發(fā)生變化,AngularJS會實時更新結(jié)合點,隨之更新視圖。比如,視圖組件被AngularJS用下面這個模板構(gòu)建出來:<htmlng-app>

<head>

...

<scriptsrc="lib/angular/angular.js"></script>

<scriptsrc="js/controllers.js"></script>

</head>

<bodyng-controller="PhoneListCtrl">

<ul>

<ling-repeat="phoneinphones">

{{}}

<p>{{phone.snippet}}</p>

</li>

</ul>

</body>

</html>

我們剛剛把靜態(tài)編碼的手機列表替換掉了,因為這里我們使用\hngRepeat指令和兩個用花括號包裹起來的AngularJS表達式——{{}}和{{phone.snippet}}——能達到同樣的效果。在<li>標簽里面的ng-repeat="phoneinphones"語句是一個AngularJS迭代器。這個迭代器告訴AngularJS用第一個<li>標簽作為模板為列表中的每一部手機創(chuàng)建一個<li>元素。正如我們在第0步時學(xué)到的,包裹在和phone.snippet周圍的花括號標識著數(shù)據(jù)綁定。和常量計算不同的是,這里的表達式實際上是我們應(yīng)用的一個數(shù)據(jù)模型引用,這些我們在PhoneListCtrl控制器里面都設(shè)置好了。模型和控制器在PhoneListCtrl控制器里面初始化了數(shù)據(jù)模型(這里只不過是一個包含了數(shù)組的函數(shù),數(shù)組中存儲的對象是手機數(shù)據(jù)列表):app/js/controller.js:functionPhoneListCtrl($scope){

$scope.phones=[

{"name":"NexusS",

"snippet":"FastjustgotfasterwithNexusS."},

{"name":"MotorolaXOOM?withWi-Fi",

"snippet":"TheNext,NextGenerationtablet."},

{"name":"MOTOROLAXOOM?",

"snippet":"TheNext,NextGenerationtablet."}

];

}

盡管控制器看起來并沒有起到什么控制的作用,但是它在這里起到了至關(guān)重要的作用。通過給定我們數(shù)據(jù)模型的語境,控制器允許我們建立模型和視圖之間的數(shù)據(jù)綁定。我們是這樣把表現(xiàn)層,數(shù)據(jù)和邏輯部件聯(lián)系在一起的:PhoneListCtrl——控制器方法的名字(在JS文件controllers.js中)和<body>標簽里面的\hngController指令的值相匹配。手機的數(shù)據(jù)此時與注入到我們控制器函數(shù)的作用域($scope)相關(guān)聯(lián)。當應(yīng)用啟動之后,會有一個根作用域被創(chuàng)建出來,而控制器的作用域是根作用域的一個典型后繼。這個控制器的作用域?qū)λ?lt;bodyng-controller="PhoneListCtrl">標記內(nèi)部的數(shù)據(jù)綁定有效。AngularJS的作用域理論非常重要:一個作用域可以視作模板、模型和控制器協(xié)同工作的粘接器。AngularJS使用作用域,同時還有模板中的信息,數(shù)據(jù)模型和控制器。這些可以幫助模型和視圖分離,但是他們兩者確實是同步的!任何對于模型的更改都會即時反映在視圖上;任何在視圖上的更改都會被立刻體現(xiàn)在模型中。想要更加深入理解AngularJS的作用域,請參看\hAngularJS作用域文檔。測試“AngularJS方式”讓開發(fā)時代碼測試變得十分簡單。讓我們來瞅一眼下面這個為控制器新添加的單元測試:test/unit/controllersSpec.js:describe('PhoneCatcontrollers',function(){

describe('PhoneListCtrl',function(){

it('shouldcreate"phones"modelwith3phones',function(){

varscope={},

ctrl=newPhoneListCtrl(scope);

expect(scope.phones.length).toBe(3);

});

});

});

這個測試驗證了我們的手機數(shù)組里面有三條記錄(暫時無需弄明白這個測試腳本)。這個例子顯示出為AngularJS的代碼創(chuàng)建一個單元測試是多么的容易。正因為測試在軟件開發(fā)中是必不可少的環(huán)節(jié),所以我們使得在AngularJS可以輕易地構(gòu)建測試,來鼓勵開發(fā)者多寫它們。在寫測試的時候,AngularJS的開發(fā)者傾向于使用Jasmine行為驅(qū)動開發(fā)(BBD)框架中的語法。盡管AngularJS沒有強迫你使用Jasmine,但是我們在教程里面所有的測試都使用Jasmine編寫。你可以在Jasmine的\h官方主頁或者\hJasmineWiki上獲得相關(guān)知識?;贏ngularJS的項目被預(yù)先配置為使用\hJsTestDriver來運行單元測試。你可以像下面這樣運行測試:在一個單獨的終端上,進入到angular-phonecat目錄并且運行./scripts/test-server.sh來啟動測試(Windows命令行下請輸入.\scripts\test-server.bat來運行腳本,后面腳本命令運行方式類似);打開一個新的瀏覽器窗口,并且轉(zhuǎn)到\hhttp://localhost:9876;選擇“Capturethisbrowserinstrictmode”。這個時候,你可以拋開你的窗口不管然后把這事忘了。JsTestDriver會自己把測試跑完并且把結(jié)果輸出在你的終端里。運行./scripts/test.sh進行測試。你應(yīng)當看到類似于如下的結(jié)果:Chrome:Runnerreset.

.

Total1tests(Passed:1;Fails:0;Errors:0)(2.00ms)

Chrome19.0.1084.36MacOS:Run1tests(Passed:1;Fails:0;Errors0)(2.00ms)

耶!測試通過了!或者沒有...注意:如果在你運行測試之后發(fā)生了錯誤,關(guān)閉瀏覽器然后回到終端關(guān)了腳本,然后在重新來一邊上面的步驟。練習(xí)為index.html添加另一個數(shù)據(jù)綁定。例如:<p>Totalnumberofphones:{{phones.length}}</p>

創(chuàng)建一個新的數(shù)據(jù)模型屬性,并且把它綁定到模板上。例如:$scope.hello="Hello,World!"

更新你的瀏覽器,確保顯示出來“Hello,World!”用一個迭代器創(chuàng)建一個簡單的表:<table>

<tr><th>rownumber</th></tr>

<trng-repeat="iin[0,1,2,3,4,5,6,7]"><td>{{i}}</td></tr>

</table>

現(xiàn)在讓數(shù)據(jù)模型表達式的i增加1:<table>

<tr><th>rownumber</th></tr>

<trng-repeat="iin[0,1,2,3,4,5,6,7]"><td>{{i+1}}</td></tr>

</table>

確定把toBe(3)改成toBe(4)之后單元測試失敗,然后重新跑一遍./scripts/test.sh腳本總結(jié)你現(xiàn)在擁有一個模型,視圖,控制器分離的動態(tài)應(yīng)用了,并且你隨時進行了測試?,F(xiàn)在,你可以進入到\h步驟3來為應(yīng)用加入全文檢索功能了。

AngularJS入門教程03:迭代器過濾我們在上一步做了很多基礎(chǔ)性的訓(xùn)練,所以現(xiàn)在我們可以來做一些簡單的事情嘍。我們要加入全文檢索功能(沒錯,這個真的非常簡單!)。同時,我們也會寫一個端到端測試,因為一個好的端到端測試可以幫上很大忙。它監(jiān)視著你的應(yīng)用,并且在發(fā)生回歸的時候迅速報告。請重置工作目錄:gitcheckout-fstep-3

我們的應(yīng)用現(xiàn)在有了一個搜索框。注意到頁面上的手機列表隨著用戶在搜索框中的輸入而變化。步驟2和步驟3之間最重要的不同在下面列出。你可以在\hGitHub里看到完整的差別??刂破魑覀儗刂破鞑蛔鋈魏涡薷摹D0錫pp/index.html<divclass="container-fluid">

<divclass="row-fluid">

<divclass="span2">

<!--Sidebarcontent-->

Search:<inputng-model="query">

</div>

<divclass="span10">

<!--Bodycontent-->

<ulclass="phones">

<ling-repeat="phoneinphones|filter:query">

{{}}

<p>{{phone.snippet}}</p>

</li>

</ul>

</div>

</div>

</div>

我們現(xiàn)在添加了一個<input>標簽,并且使用AngularJS的\h$filter函數(shù)來處理\hngRepeat指令的輸入。這樣允許用戶輸入一個搜索條件,立刻就能看到對電話列表的搜索結(jié)果。我們來解釋一下新的代碼:數(shù)據(jù)綁定:這是AngularJS的一個核心特性。當頁面加載的時候,AngularJS會根據(jù)輸入框的屬性值名字,將其與數(shù)據(jù)模型中相同名字的變量綁定在一起,以確保兩者的同步性。在這段代碼中,用戶在輸入框中輸入的數(shù)據(jù)名字稱作query,會立刻作為列表迭代器(phoneinphones|filter:query`)其過濾器的輸入。當數(shù)據(jù)模型引起迭代器輸入變化的時候,迭代器可以高效得更新DOM將數(shù)據(jù)模型最新的狀態(tài)反映出來。使用filter過濾器:\hfilter函數(shù)使用query的值來創(chuàng)建一個只包含匹配query記錄的新數(shù)組。ngRepeat會根據(jù)filter過濾器生成的手機記錄數(shù)據(jù)數(shù)組來自動更新視圖。整個過程對于開發(fā)者來說都是透明的。測試在步驟2,我們學(xué)習(xí)了編寫和運行一個測試的方法。單元測試用來測試我們用js編寫的控制器和其他組件都非常方便,但是不能方便的對DOM操作和應(yīng)用集成進行測試。對于這些來說,端到端測試是一個更好的選擇。搜索特性是完全通過模板和數(shù)據(jù)綁定實現(xiàn)的,所以我們的第一個端到端測試就來驗證這些特性是否符合我們的預(yù)期。test/e2e/scenarios.js:describe('PhoneCatApp',function(){

describe('Phonelistview',function(){

beforeEach(function(){

browser().navigateTo('../../app/index.html');

});

it('shouldfilterthephonelistasusertypesintothesearchbox',function(){

expect(repeater('.phonesli').count()).toBe(3);

input('query').enter('nexus');

expect(repeater('.phonesli').count()).toBe(1);

input('query').enter('motorola');

expect(repeater('.phonesli').count()).toBe(2);

});

});

});

盡管這段測試代碼的語法看起來和我們之前用Jasmine寫的單元測試非常像,但是端到端測試使用的是\hAngularJS端到端測試器提供的接口。運行一個端到端測試,在瀏覽器新標簽頁中打開下面任意一個:node.js用戶:\hhttp://localhost:8000/test/e2e/runner.html使用其他http服務(wù)器的用戶:http://localhost:[port-number]/[context-path]/test/e2e/runner.html訪客:\h/angular-phonecat/step-3/test/e2e/runner.html這個測試驗證了搜素框和迭代器被正確地集成起來。你可以發(fā)現(xiàn),在AngularJS里寫一個端到端測試多么的簡單。盡管這個例子僅僅是一個簡單的測試,但是用它來構(gòu)建任何一個復(fù)雜、可讀的端到端測試都很容易。練習(xí)在index.html模板中添加一個{{query}}綁定來實時顯示query模型的當前值,然后觀察他們是如何根據(jù)輸入框中的值而變化?,F(xiàn)在我們來看一下我們怎么讓query模型的值出現(xiàn)在HTML的頁面標題上。你或許認為像下面這樣在title標簽上加上一個綁定就行了:<title>GooglePhoneGallery:{{query}}</title>

但是,當你重載頁面的時候,你根本沒辦法得到期望的結(jié)果。這是因為query模型僅僅在body元素定義的作用域內(nèi)才有效。<bodyng-controller="PhoneListCtrl">

如果你想讓<title>元素綁定上query模型,你必須把ngController聲明移動到HTML元素上,因為它是title和body元素的共同祖先。<htmlng-appng-controller="PhoneListCtrl">

一定要注意把body元素上的ng-controller聲明給刪了。當綁定兩個花括號在title元素上可以實現(xiàn)我們的目標,但是你或許發(fā)現(xiàn)了,頁面正加載的時候它們已經(jīng)顯示給用戶看了。一個更好的解決方案是使用\hngBind或者\hngBindTemplate指令,它們在頁面加載時對用戶是不可見的:<titleng-bind-template="GooglePhoneGallery:{{query}}">GooglePhoneGallery</title>

在test/e2e/scenarios.js的describe塊中加入下面這些端到端測試代碼:it('shoulddisplaythecurrentfiltervaluewithinanelementwithid"status"',

function(){

expect(element('#status').text()).toMatch(/Currentfilter:\s*$/);

input('query').enter('nexus');

expect(element('#status').text()).toMatch(/Currentfilter:nexus\s*$/);

//alternativeversionofthelastassertionthattestsjustthevalueofthebinding

using('#status').expect(binding('query')).toBe('nexus');

});

刷新瀏覽器,端到端測試器會報告測試失敗。為了讓測試通過,編輯index.html,添加一個id為“status”的div或者p元素,內(nèi)容是一個query綁定,再加上Currentfilter:前綴。例如:<divid="status">Currentfilter:{{query}}</div>

在端到端測試里面加一條pause();語句,重新跑一遍。你將發(fā)現(xiàn)測試器暫停了!這樣允許你有機會在測試運行過程中查看你應(yīng)用的狀態(tài)。測試應(yīng)用是實時的!你可以更換搜索內(nèi)容來證明。稍有經(jīng)驗?zāi)憔蜁溃@對于在端到端測試中迅速找到問題是多么的關(guān)鍵。總結(jié)我們現(xiàn)在添加了全文搜索功能,并且完成一個測試證明了搜索是對的!現(xiàn)在讓我們繼續(xù)到\h步驟4來看看給我們的手機應(yīng)用增加排序功能。

AngularJS入門教程04:雙向綁定在這一步你會增加一個讓用戶控制手機列表顯示順序的特性。動態(tài)排序可以這樣實現(xiàn),添加一個新的模型屬性,把它和迭代器集成起來,然后讓數(shù)據(jù)綁定完成剩下的事情。請重置工作目錄:gitcheckout-fstep-4

你應(yīng)該發(fā)現(xiàn)除了搜索框之外,你的應(yīng)用多了一個下來菜單,它可以允許控制電話排列的順序。步驟3和步驟4之間最重要的不同在下面列出。你可以在\hGitHub里看到完整的差別。模板app/index.htmlSearch:<inputng-model="query">

Sortby:

<selectng-model="orderProp">

<optionvalue="name">Alphabetical</option>

<optionvalue="age">Newest</option>

</select>

<ulclass="phones">

<ling-repeat="phoneinphones|filter:query|orderBy:orderProp">

{{}}

<p>{{phone.snippet}}</p>

</li>

</ul>

我們在index.html中做了如下更改:首先,我們增加了一個叫做orderProp的<select>標簽,這樣我們的用戶就可以選擇我們提供的兩種排序方法。然后,在filter過濾器后面添加一個\horderBy過濾器用其來處理進入迭代器的數(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:functionPhoneListCtrl($scope){

$scope.phones=[

{"name":"NexusS",

"snippet":"FastjustgotfasterwithNexusS.",

"age":0},

{"name":"MotorolaXOOM?withWi-Fi",

"snippet":"TheNext,NextGenerationtablet.",

"age":1},

{"name":"MOTOROLAXOOM?",

"snippet":"TheNext,NextGenerationtablet.",

"age":2}

];

$scope.orderProp='age';

}

我們修改了phones模型——手機的數(shù)組——為每一個手機記錄其增加了一個age屬性。我們會根據(jù)age屬性來對手機進行排序。我們在控制器代碼里加了一行讓orderProp的默認值為age。如果我們不設(shè)置默認值,這個模型會在我們的用戶在下拉菜單選擇一個順序之前一直處于未初始化狀態(tài)。現(xiàn)在我們該好好談?wù)勲p向數(shù)據(jù)綁定了。注意到當應(yīng)用在瀏覽器中加載時,“Newest”在下拉菜單中被選中。這是因為我們在控制器中把orderProp設(shè)置成了‘a(chǎn)ge’。所以綁定在從我們模型到用戶界面的方向上起作用——即數(shù)據(jù)從模型到視圖的綁定?,F(xiàn)在當你在下拉菜單中選擇“Alphabetically”,數(shù)據(jù)模型會被同時更新,并且手機列表數(shù)組會被重新排序。這個時候數(shù)據(jù)綁定從另一個方向產(chǎn)生了作用——即數(shù)據(jù)從視圖到模型的綁定。測試我們所做的更改可以通過一個單元測試或者一個端到端測試來驗證正確性。我們首先來看看單元測試:test/unit/controllersSpec.js:describe('PhoneCatcontrollers',function(){

describe('PhoneListCtrl',function(){

varscope,ctrl;

beforeEach(function(){

scope={},

ctrl=newPhoneListCtrl(scope);

});

it('shouldcreate"phones"modelwith3phones',function(){

expect(scope.phones.length).toBe(3);

});

it('shouldsetthedefaultvalueoforderPropmodel',function(){

expect(scope.orderProp).toBe('age');

});

});

});

單元測試現(xiàn)在驗證了默認值被正確設(shè)置。我們使用Jasmine的接口把PhoneListCtrl控制器提取到一個beforeEach塊中,這個塊會被所有的父塊describe中的所有測試所共享。運行這些單元測試,跟以前一樣,執(zhí)行./scripts/test.sh腳本,你應(yīng)該會看到如下輸出(注意:要在瀏覽器打開\hhttp://localhost:9876并進入嚴格模式,測試才會運行?。篊hrome:Runnerreset.

..

Total2tests(Passed:2;Fails:0;Errors:0)(3.00ms)

Chrome19.0.1084.36MacOS:Run2tests(Passed:2;Fails:0;Errors0)(3.00ms)

現(xiàn)在我們把注意力轉(zhuǎn)移到端到端測試上來。test/e2e/scenarios.js:...

it('shouldbepossibletocontrolphoneorderviathedropdownselectbox',

function(){

//let'snarrowthedatasettomakethetestassertionsshorter

input('query').enter('tablet');

expect(repeater('.phonesli','PhoneList').column('')).

toEqual(["MotorolaXOOM\u2122withWi-Fi",

"MOTOROLAXOOM\u2122"]);

select('orderProp').option('Alphabetical');

expect(repeater('.phonesli','PhoneList').column('')).

toEqual(["MOTOROLAXOOM\u2122",

"MotorolaXOOM\u2122withWi-Fi"]);

});

...

端到端測試驗證了選項框的排序機制是正確的。你現(xiàn)在可以刷新你的瀏覽器,然后重新跑一遍端到端測試,或者你可以在\hAngularJS的服務(wù)器上運行一下。練習(xí)在PhoneListCtrl控制器中,把設(shè)置orderProp那條語句刪掉,你會看到AngularJS會在下拉菜單中臨時添加一個空白的選項,并且排序順序是默認排序(即未排序)。在index.html模板里面添加一個`{{orderProp}}綁定來實時顯示它的值??偨Y(jié)現(xiàn)在你已經(jīng)為你的應(yīng)用提供了搜索功能,并且完整的進行了測試。\h步驟5我們將學(xué)習(xí)AngularJS的服務(wù)以及AngularJS如何使用依賴注入。

AngularJS入門教程05:XHR和依賴注入到現(xiàn)在為止,我們使用是硬編碼的三條手機記錄數(shù)據(jù)集?,F(xiàn)在我們使用AngularJS一個內(nèi)置服務(wù)\h$http來獲取一個更大的手機記錄數(shù)據(jù)集。我們將使用AngularJS的\h依賴注入(dependencyinjection(DI))功能來為PhoneListCtrl控制器提供這個AngularJS服務(wù)。請重置工作目錄:gitcheckout-fstep-5

刷新瀏覽器,你現(xiàn)在應(yīng)該能看到一個20部手機的列表。步驟4和步驟5之間最重要的不同在下面列出。你可以在\hGitHub里看到完整的差別。數(shù)據(jù)你項目當中的app/phones/phones.json文件是一個數(shù)據(jù)集,它以JSON格式存儲了一張更大的手機列表。下面是這個文件的一個樣例:[

{

"age":13,

"id":"motorola-defy-with-motoblur",

"name":"MotorolaDEFY\u2122withMOTOBLUR\u2122",

"snippet":"Areyoureadyforeverythinglifethrowsyourway?"

...

},

...

]

控制器我們在控制器中使用AngularJS服務(wù)\h$http向你的Web服務(wù)器發(fā)起一個HTTP請求,以此從app/phones/phones.json文件中獲取數(shù)據(jù)。$http僅僅是AngularJS眾多內(nèi)建服務(wù)中之一,這些服務(wù)可以處理一些Web應(yīng)用的通用操作。AngularJS能將這些服務(wù)注入到任何你需要它們的地方。服務(wù)是通過AngularJS的\h依賴注入DI子系統(tǒng)來管理的。依賴注入服務(wù)可以使你的Web應(yīng)用良好構(gòu)建(比如分離表現(xiàn)層、數(shù)據(jù)和控制三者的部件)并且松耦合(一個部件自己不需要解決部件之間的依賴問題,它們都被DI子系統(tǒng)所處理)。app/js/controllers.jsfunctionPhoneListCtrl($scope,$http){

$http.get('phones/phones.json').success(function(data){

$scope.phones=data;

});

$scope.orderProp='age';

}

//PhoneListCtrl.$inject=['$scope','$http'];

$http向Web服務(wù)器發(fā)起一個HTTPGET請求,索取phone/phones.json(注意,url是相對于我們的index.html文件的)。服務(wù)器用json文件中的數(shù)據(jù)作為響應(yīng)。(這個響應(yīng)或許是實時從后端服務(wù)器動態(tài)產(chǎn)生的。但是對于瀏覽器來說,它們看起來都是一樣的。為了簡單起見,我們在教程里面簡單地使用了一個json文件。)$http服務(wù)用success返回[對象應(yīng)答][ng.$q]。當異步響應(yīng)到達時,用這個對象應(yīng)答函數(shù)來處理服務(wù)器響應(yīng)的數(shù)據(jù),并且把數(shù)據(jù)賦值給作用域的phones數(shù)據(jù)模型。注意到AngularJS會自動檢測到這個json應(yīng)答,并且已經(jīng)為我們解析出來了!為了使用AngularJS的服務(wù),你只需要在控制器的構(gòu)造函數(shù)里面作為參數(shù)聲明出所需服務(wù)的名字,就像這樣:functionPhoneListCtrl($scope,$http){...}

當控制器構(gòu)造的時候,AngularJS的依賴注入器會將這些服務(wù)注入到你的控制器中。當然,依賴注入器也會處理所需服務(wù)可能存在的任何傳遞性依賴(一個服務(wù)通常會依賴于其他的服務(wù))。注意到參數(shù)名字非常重要,因為注入器會用他們?nèi)ふ蚁鄳?yīng)的依賴。'$'前綴命名習(xí)慣你可以創(chuàng)建自己的服務(wù),實際上我們在\h步驟11就會學(xué)習(xí)到它。作為一個命名習(xí)慣,AngularJS內(nèi)建服務(wù),作用域方法,以及一些其他的AngularJSAPI都在名字前面使用一個‘$’前綴。不要使用‘$’前綴來命名你自己的服務(wù)和模型,否則可能會產(chǎn)生名字沖突。關(guān)于JS壓縮由于AngularJS是通過控制器構(gòu)造函數(shù)的參數(shù)名字來推斷依賴服務(wù)名稱的。所以如果你要\h壓縮PhoneListCtrl控制器的JS代碼,它所有的參數(shù)也同時會被壓縮,這時候依賴注入系統(tǒng)就不能正確的識別出服務(wù)了。為了克服壓縮引起的問題,只要在控制器函數(shù)里面給$inject屬性賦值一個依賴服務(wù)標識符的數(shù)組,就像被注釋掉那段最后一行那樣:PhoneListCtrl.$inject=['$scope','$http'];

另一種方法也可以用來指定依賴列表并且避免壓縮問題——使用Javascript數(shù)組方式構(gòu)造控制器:把要注入的服務(wù)放到一個字符串數(shù)組(代表依賴的名字)里,數(shù)組最后一個元素是控制器的方法函數(shù):varPhoneListCtrl=['$scope','$http',function($scope,$http){/*constructorbody*/}];

上面提到的兩種方法都能和AngularJS可注入的任何函數(shù)完美協(xié)作,要選哪一種方式完全取決于你們項目的編程風(fēng)格,建議使用數(shù)組方式。測試test/unit/controllerSpec.js:由于我們現(xiàn)在開始使用依賴注入,并且我們的控制器也含有了許多依賴服務(wù),所以為我們的控制器構(gòu)造測試就有一點小小的復(fù)雜了。我們需要使用new操作并且提供給構(gòu)造器包括$http的一些偽實現(xiàn)。然而,我們推薦的方法(而且更加簡單噢)是在測試環(huán)境下創(chuàng)建一個控制器,使用的方法和AngularJS在產(chǎn)品代碼于下面的場景下做的一樣:describe('PhoneCatcontrollers',function(){

describe('PhoneListCtrl',function(){

varscope,ctrl,$httpBackend;

beforeEach(inject(function(_$httpBackend_,$rootScope,$controller){

$httpBackend=_$httpBackend_;

$httpBackend.expectGET('phones/phones.json').

respond([{name:'NexusS'},{name:'MotorolaDROID'}]);

scope=$rootScope.$new();

ctrl=$controller(PhoneListCtrl,{$scope:scope});

}));

注意:因為我們在測試環(huán)境中加載了Jasmine和angular-mock.js,我們有了兩個輔助方法,\hmodule和\hinject,來幫助我們獲得和配置注入器。用如下方法,我們在測試環(huán)境中創(chuàng)建一個控制器:我們使用inject方法將\h$rootScope,\h$controller和\h$httpBackend服務(wù)實例注入到Jasmine的beforeEach函數(shù)里。這些實例都來自一個注入器,但是這個注入器在每一個測試內(nèi)部都會被重新創(chuàng)建。這樣保證了每一個測試都從一個周知的起始點開始,并且每一個測試都和其他測試相互獨立。調(diào)用$rootScope.$new()來為我們的控制器創(chuàng)建一個新的作用域。PhoneListCtrl函數(shù)和剛創(chuàng)建的作用域作為參數(shù),傳遞給已注入的$controller函數(shù)。由于我們現(xiàn)在的代碼在創(chuàng)建PhoneListCtrl子作用域之前,于控制器中使用$http服務(wù)獲取了手機列表數(shù)據(jù),我們需要告訴測試套件等待一個從控制器來的請求。我們可以這樣做:將請求服務(wù)$httpBackend注入到我們的beforeEach函數(shù)中。這是這個服務(wù)的一個偽版本,這樣做在產(chǎn)品環(huán)境中有助于處理所有的XHR和JSONP請求。服務(wù)的偽版本允許你不用考慮原生API和全局狀態(tài)——隨便一個都能構(gòu)成測試的噩夢——就可以寫測試。使用$httpBackend.expectGET方法來告訴$httpBackend服務(wù)來等待一個HTTP請求,并且告訴它如何對其進行響應(yīng)。注意到,當我們調(diào)用$httpBackend.flush方法之前,響應(yīng)是不會被發(fā)出的?,F(xiàn)在,it('shouldcreate"phones"modelwith2phonesfetchedfromxhr',function(){

expect(scope.phones).toBeUndefined();

$httpBackend.flush();

expect(scope.phones).toEqual([{name:'NexusS'},

{name:'MotorolaDROID'}]);

});

在瀏覽器里,我們調(diào)用$httpBackend.flush()來清空(flush)請求隊列。這樣會使得$http服務(wù)返回的promise(什么是promise請參見\h這里)能夠被解釋成規(guī)范的應(yīng)答。我們設(shè)置一些斷言,來驗證手機數(shù)據(jù)模型已經(jīng)在作用域里了。最終,我們驗證orderProp的默認值被正確設(shè)置:it('shouldsetthedefaultvalueoforderPropmodel',function(){

expect(scope.orderProp).toBe('age');

});

;

執(zhí)行./scripts/test.sh腳本來運行測試,你應(yīng)該會看到如下輸出:Chrome:Runnerreset.

..

Total2tests(Passed:2;Fails:0;Errors:0)(3.00ms)

Chrome19.0.1084.36MacOS:Run2tests(Passed:2;Fails:0;Errors0)(3.00ms)

練習(xí)在index.html末尾添加一個{{phones|json}}綁定,觀察json格式的手機列表。在PhoneListCtrl控制器中,把HTTP應(yīng)答預(yù)處理一下,使得只顯示手機列表的前五個。在$http回調(diào)函數(shù)里面使用如下代碼:$scope.phones=data.splice(0,5);

總結(jié)現(xiàn)在你應(yīng)該感覺得到使用AngularJS的服務(wù)是多么的容易(這都要歸功于AngularJS服務(wù)的依賴注入機制),轉(zhuǎn)到\h步驟6,你會為手機添加縮略圖和鏈接。

AngularJS入門教程06:鏈接與圖片模板這一步,你會為手機列表的手機添加縮略圖以及一些鏈接,不過這些鏈接還不會起作用。接下來你會使用這些鏈接來分類顯示手機的額外信息。請重置工作目錄:gitcheckout-fstep-6

現(xiàn)在你應(yīng)該能夠看到列表里面手機的圖片和鏈接了。步驟5和步驟6之間最重要的不同在下面列出。你可以在\hGitHub里看到完整的差別。數(shù)據(jù)注意到現(xiàn)在phones.json文件包含了唯一標識符和每一部手機的圖像鏈接。這些url現(xiàn)在指向app/img/phones/目錄。app/phones/phones.json(樣例片段)[

{

...

"id":"motorola-defy-with-motoblur",

"imageUrl":"img/phones/motorola-defy-with-motoblur.0.jpg",

"name":"MotorolaDEFY\u2122withMOTOBLUR\u2122",

...

},

...

]

模板app/index.html...

<ulclass="phones">

<ling-repeat="phoneinphones|filter:query|orderBy:orderProp"class="thumbnail">

<ahref="#/phones/{{phone.id}}"class="thumb"><imgng-src="{{phone.imageUrl}}"></a>

<ahref="#/phones/{{phone.id}}">{{}}</a>

<p>{{phone.snippet}}</p>

</li>

</ul>

...

這些鏈接將來會指向每一部電話的詳細信息頁。不過現(xiàn)在為了產(chǎn)生這些鏈接,我們在href屬性里面使用我們早已熟悉的雙括號數(shù)據(jù)綁定。在步驟2,我們添加了{{}}綁定作為元素內(nèi)容。在這一步,我們在元素屬性中使用{{phone.id}}綁定。我們同樣為每條記錄添加手機圖片,只需要使用\hngSrc指令代替<img>的src屬性標簽就可以了。如果我們僅僅用一個正常src屬性來進行綁定(<imgclass="diagram"src="{{phone.imageUrl}}">),瀏覽器會把AngularJS的{{表達式}}標記直接進行字面解釋,并且發(fā)起一個向非法urlhttp://localhost:8000/app/{{phone.imageUrl}}的請求。因為瀏覽器載入頁面時,同時也會請求載入圖片,AngularJS在頁面載入完畢時才開始編譯——瀏覽器請求載入圖片時{{phone.imageUrl}}還沒得到編譯!有了這個ngSrc指令會避免產(chǎn)生這種情況,使用ngSrc指令防止瀏覽器產(chǎn)生一個指向非法地址的請求。測試test/e2e/scenarios.js...

it('shouldrenderphonespecificlinks',function(){

input('query').enter('nexus');

element('.phoneslia').click();

expect(browser().location().url()).toBe('/phones/nexus-s');

});

...

我們添加了一個新的端到端測試來驗證應(yīng)用為手機視圖產(chǎn)生了正確的鏈接,上面就是我們的實現(xiàn)。你現(xiàn)在可以刷新你的瀏覽器,并且用端到端測試器來觀察測試的運行,或者你可以在\hAngularJS服務(wù)器上運行它們。練習(xí)將ng-src指令換成普通的src屬性。用像Firebug,ChromeWebInspector這樣的工具,或者直接去看服務(wù)器的訪問日志,你會發(fā)現(xiàn)你的應(yīng)用向/app/%7B%7Bphone.imageUrl%7D%7D(或者/app/{{phone.imageUrl}})發(fā)送了一個非法請求。這個問題是由于瀏覽器會在遇到img標簽的時候立刻向還未得到編譯的URL地址發(fā)送一個請求,AngularJS只有在頁面載入完畢后才開始編譯表達式從而得到正確的圖片URL地址??偨Y(jié)如今你已經(jīng)添加了手機圖片和鏈接,轉(zhuǎn)到\h步驟7,我們將學(xué)習(xí)AngularJS的布局模板以及AngularJS是如何輕易地為應(yīng)用提供多重視圖。

AngularJS入門教程07:路由與多視圖在這一步,你將學(xué)習(xí)如何創(chuàng)建一個布局模板并且通過路由功能來構(gòu)建一個具有多個視圖的應(yīng)用。請重置工作目錄:gitcheckout-fstep-7

注意到現(xiàn)在當你轉(zhuǎn)到app/index.html時,你會被重定向到app/index.html#/phones并且相同的手機列表在瀏覽器中顯示了出來。當你點擊一個手機鏈接時,一個手機詳細信息列表也被顯示了出來。步驟6和步驟7之間最重要的不同在下面列出。你可以在\hGitHub里看到完整的差別。多視圖,路由和布局模板我們的應(yīng)用正慢慢發(fā)展起來并且變得逐漸復(fù)雜。在步驟7之前,應(yīng)用只給我們的用戶提供了一個簡單的界面(一張所有手機的列表),并且所有的模板代碼位于index.html文件中。下一步是增加一個能夠顯示我們列表中每一部手機詳細信息的頁面。為了增加詳細信息視圖,我們可以拓展index.html來同時包含兩個視圖的模板代碼,但是這樣會很快給我們帶來巨大的麻煩。相反,我們要把index.html模板轉(zhuǎn)變成“布局模板”。這是我們應(yīng)用所有視圖的通用模板。其他的“局部布局模板”隨后根據(jù)當前的“路由”被充填入,從而形成一個完整視圖展示給用戶。AngularJS中應(yīng)用的路由通過\h$routeProvider來聲明,它是\h$route服務(wù)的提供者。這項服務(wù)使得控制器、視圖模板與當前瀏覽器的URL可以輕易集成。應(yīng)用這個特性我們就可以實現(xiàn)\h深鏈接,它允許我們使用瀏覽器的歷史(回退或者前進導(dǎo)航)和書簽。關(guān)于依賴注入(DI),注入器(Injector)和服務(wù)提供者(Providers)正如從前面你學(xué)到的,\h依賴注入是AngularJS的核心特性,所以你必須要知道一點這家伙是怎么工作的。當應(yīng)用引導(dǎo)時,AngularJS會創(chuàng)建一個注入器,我們應(yīng)用后面所有依賴注入的服務(wù)都會需要它。這個注入器自己并不知道$http和$route是干什么的,實際上除非它在模塊定義的時候被配置過,否則它根本都不知道這些服務(wù)的存在。注入器唯一的職責(zé)是載入指定的服務(wù)模塊,在這些模塊中注冊所有定義的服務(wù)提供者,并且當需要時給一個指定的函數(shù)注入依賴(服務(wù))。這些依賴通過它們的提供者“懶惰式”(需要時才加載)實例化。提供者是提供(創(chuàng)建)服務(wù)實例并且對外提供API接口的對象,它可以被用來控制一個服務(wù)的創(chuàng)建和運行時行為。對于$route服務(wù)來說,$routeProvider對外提供了API接口,通過API接口允許你為你的應(yīng)用定義路由規(guī)則。AngularJS模塊解決了從應(yīng)用中刪除全局狀態(tài)和提供方法來配置注入器這兩個問題。和AMD或者require.js這兩個模塊(非AngularJS的兩個庫)不同的是,AngularJS模塊并沒有試圖去解決腳本加載順序以及懶惰式腳本加載這樣的問題。這些目標和AngularJS要解決的問題毫無關(guān)聯(lián),所以這些模塊完全可以共存來實現(xiàn)各自的目標。App模塊app/js/app.jsangular.module('phonecat',[]).

config(['$routeProvider',function($routeProvider){

$routeProvider.

when('/phones',{templateUrl:'partials/phone-list.html',controller:PhoneListCtrl}).

when('/phones/:phoneId',{templateUrl:'partials/phone-detail.html',controller:PhoneDetailCtrl}).

otherwise({redirectTo:'/phones'});

}]);

為了給我們的應(yīng)用配置路由,我們需要給應(yīng)用創(chuàng)建一個模塊。我們管這個模塊叫做phonecat,并且通過使用configAPI,我們請求把$routeProvider注入到我們的配置函數(shù)并且使用$routeProvider.whenAPI來定義我們的路由規(guī)則。注意到在注入器配置階段,提供者也可以同時被注入,但是一旦注入器被創(chuàng)建并且開始創(chuàng)建服務(wù)實例的時候,他們就不再會被外界所獲取到。我們的路由規(guī)則定義如下當URL映射段為/phones時,手機列表視圖會被顯示出來。為了構(gòu)造這個視圖,AngularJS會使用phone-list.html模板和PhoneListCtrl控制器。當URL映射段為/phone/:phoneId時,手機詳細信息視圖被顯示出來。這里:phoneId是URL的變量部分。為了構(gòu)造手機詳細視圖,AngularJS會使用phone-detail.html模板和PhoneDetailCtrl控制器。我們重用之前創(chuàng)造過的PhoneListCtrl控制器,同時我們?yōu)槭謾C詳細視圖添加一個新的PhoneDetailCtrl控制器,把它存放在app/js/controllers.js文件里。$route.otherwise({redirectTo:'/phones'})語句使得當瀏覽器地址不能匹配我們?nèi)魏我粋€路由規(guī)則時,觸發(fā)重定向到/phones。注意到在第二條路由聲明中:phoneId參數(shù)的使用。$route服務(wù)使用路由聲明/phones/:phoneId作為一個匹配當前URL的模板。所有以:符號聲明的變量(此處變量為phones)都會被提取,然后存放在\h$routeParams對象中。為了讓我們的應(yīng)用引導(dǎo)我們新創(chuàng)建的模塊,我們同時需要在\hngApp指令的值上指明模塊的名字:app/index.html<!doctypehtml>

<htmllang="en"ng-app="phonecat">

...

控制器app/js/controllers.js...

functionPhoneDetailCtrl($scope,$routeParams){

$scope.phoneId=$routeParams.phoneId;

}

//PhoneDetailCtrl.$inject=['$scope','$routeParams'];

模板$route服務(wù)通常和\hngView指令一起使用。ngView指令的角色是為當前路由把對應(yīng)的視圖模板載入到布局模板中。app/index.html<htmllang="en"ng-app="phonecat">

<head>

...

<scriptsrc="lib/angular/angular.js"></script>

<scriptsrc="js/app.js"></script>

<scriptsrc="js/controllers.js"></script>

</head>

<body>

<divng-view></div>

</body>

</html>

注意,我們把index.html模板里面大部分代碼移除,我們只放置了一個<div>容器,這個<div>具有ng-view屬性

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論