AngularJS

簡介

  AngularJS是爲了克服HTML在構建應用上的不足而設計的。HTML是一門很好的爲靜態文本展現設計的聲明式語言,但要構建WEB應用的話它就顯得乏力了。因此我作了一些工做(你也能夠以爲是小花招)來讓瀏覽器作我想要的事。
  一般,咱們是經過如下技術來解決靜態網頁技術在構建動態應用上的不足:
  類庫 - 類庫是一些函數的集合,它能幫助你寫WEB應用。起主導做用的是你的代碼,由你來決定什麼時候使用類庫。類庫有:jQuery等
  框架 - 框架是一種特殊的、已經實現了的WEB應用,你只須要對它填充具體的業務邏輯。這裏框架是起主導做用的,由它來根據具體的應用邏輯來調用你的代碼。框架有:knockout、sproutcore等。
  AngularJS使用了不一樣的方法,它嘗試去補足HTML自己在構建應用方面的缺陷。AngularJS經過使用咱們稱爲標識符(directives)的結構,讓瀏覽器可以識別新的語法。例如:
  使用雙大括號{{}}語法進行數據綁定;
  使用DOM控制結構來實現迭代或者隱藏DOM片斷;
  支持表單和表單的驗證;
  能將邏輯代碼關聯到相關的DOM元素上;
  能將HTML分組成可重用的組件。

端對端的解決方案

  AngularJS試圖成爲WEB應用中的一種端對端的解決方案。這意味着它不僅是你的WEB應用中的一個小部分,而是一個完整的端對端的解決方案。這會讓AngularJS在構建一個CRUD(增長Create、查詢Retrieve、更新Update、刪除Delete)的應用時顯得很「執拗」(原文爲 opinionated,意指沒有太多的其餘方式)。可是,儘管它很「執拗」,它仍然能確保它的「執拗」只是在你構建應用的起點,而且你仍能靈活變更。AngularJS的一些出衆之處以下:
  構建一個CRUD應用可能用到的所有內容包括:數據綁定、基本模板標識符、表單驗證、路由、深度連接、組件重用、依賴注入。
  測試方面包括:單元測試、端對端測試、模擬和自動化測試框架。
  具備目錄佈局和測試腳本的種子應用做爲起點。

AngularJS的可愛之處

  AngularJS經過爲開發者呈現一個更高層次的抽象來簡化應用的開發。如同其餘的抽象技術同樣,這也會損失一部分靈活性。換句話說,並非全部的應用都適合用AngularJS來作。AngularJS主要考慮的是構建CRUD應用。幸運的是,至少90%的WEB應用都是CRUD應用。可是要了解什麼適合用AngularJS構建,就得了解什麼不適合用AngularJS構建。
  如遊戲,圖形界面編輯器,這種DOM操做很頻繁也很複雜的應用,和CRUD應用就有很大的不一樣,它們不適合用AngularJS來構建。像這種狀況用一些更輕量、簡單的技術如jQuery可能會更好。

實例

AngularJS下載地址

http://angularjs.org

Hello World!

開始學習AngularJS的一個好方法是建立經典應用程序「Hello World!」:css

  1. 使用您喜好的文本編輯器,建立一個HTML文件,例如:helloworld.html。
  2. 將下面的源代碼複製到您的HTML文件。
  3. 在web瀏覽器中打開這個HTML文件。

源代碼html

<!doctype html> <html ng-app> <head> <script src="http://code.angularjs.org/angular-1.0.1.min.js"></script> </head> <body> Hello {{'World'}}! </body> </html>

請在您的瀏覽器中運行以上代碼查看效果。node

如今讓咱們仔細看看代碼,看看到底怎麼回事。 當加載該頁時,標記ng-app告訴AngularJS處理整個HTML頁並引導應用:ios

<html ng-app>

這行載入AngularJS腳本:git

<script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>

(想了解AngularJS處理整個HTML頁的細節,請看Bootstrap。)angularjs

最後,標籤中的正文是應用的模板,在UI中顯示咱們的問候語:github

Hello {{'World'}}!

注意,使用雙大括號標記{{}}的內容是問候語中綁定的表達式,這個表達式是一個簡單的字符串‘World’。web

下面,讓咱們看一個更有趣的例子:使用AngularJS對咱們的問候語文本綁定一個動態表達式。express

Hello AngularJS World!

本示例演示AngularJS的雙向數據綁定(bi-directional data binding):bootstrap

  1. 編輯前面建立的helloworld.html文檔。
  2. 將下面的源代碼複製到您的HTML文件。
  3. 刷新瀏覽器窗口。

源代碼

<!doctype html> <html ng-app> <head> <script src="http://code.angularjs.org/angular-1.0.1.min.js"></script> </head> <body> Your name: <input type="text" ng-model="yourname" placeholder="World"> <hr> Hello {{yourname || 'World'}}! </body> </html>

請在您的瀏覽器中運行以上代碼查看效果。

該示例有一下幾點重要的注意事項:

  • 文本輸入指令<input ng-model="yourname" />綁定到一個叫yourname的模型變量。
  • 雙大括號標記將yourname模型變量添加到問候語文本。
  • 你不須要爲該應用另外註冊一個事件偵聽器或添加事件處理程序!

如今試着在輸入框中鍵入您的名稱,您鍵入的名稱將當即更新顯示在問候語中。 這就是AngularJS雙向數據綁定的概念。 輸入框的任何更改會當即反映到模型變量(一個方向),模型變量的任何更改都會當即反映到問候語文本中(另外一方向)。

AngularJS應用的解析

本節描述AngularJS應用程序的三個組成部分,並解釋它們如何映射到模型-視圖-控制器設計模式:

模板(Templates)

模板是您用HTML和CSS編寫的文件,展示應用的視圖。 您可給HTML添加新的元素、屬性標記,做爲AngularJS編譯器的指令。 AngularJS編譯器是徹底可擴展的,這意味着經過AngularJS您能夠在HTML中構建您本身的HTML標記!

應用程序邏輯(Logic)和行爲(Behavior)

應用程序邏輯和行爲是您用JavaScript定義的控制器。AngularJS與標準AJAX應用程序不一樣,您不須要另外編寫偵聽器或DOM控制器,由於它們已經內置到AngularJS中了。這些功能使您的應用程序邏輯很容易編寫、測試、維護和理解。

模型數據(Data)

模型是從AngularJS做用域對象的屬性引伸的。模型中的數據多是Javascript對象、數組或基本類型,這都不重要,重要的是,他們都屬於AngularJS做用域對象。

AngularJS經過做用域來保持數據模型與視圖界面UI的雙向同步。一旦模型狀態發生改變,AngularJS會當即刷新反映在視圖界面中,反之亦然。

此外,AngularJS還提供了一些很是有用的服務特性:

  1. 底層服務包括依賴注入,XHR、緩存、URL路由和瀏覽器抽象服務。
  2. 您還能夠擴展和添加本身特定的應用服務。
  3. 這些服務可讓您很是方便的編寫WEB應用。

引導程序

咱們如今開始準備編寫AngularJS應用——phonecat。這一步驟(步驟0),您將會熟悉重要的源代碼文件,學習啓動包含AngularJS種子項目的開發環境,並在瀏覽器端運行應用。

  1. 進入angular-phonecat目錄,運行以下命令:

    git checkout -f step-0

    該命令將重置phonecat項目的工做目錄,建議您在每一學習步驟運行此命令,將命令中的數字改爲您學習步驟對應的數字,該命令將清除您在工做目錄內作的任何更改。

  2. 運行如下命令:

    node scripts/web-server.js

    來啓動服務器,啓動後命令行終端將會提示Http Server running at http://localhost:8000,請不要關閉該終端,關閉該終端即關閉了服務器。在瀏覽器中輸入http://localhost:8000/app/index.html來訪問咱們的phonecat應用。

如今,在瀏覽器中您應該已經看到了咱們的初始應用,很簡單,但說明咱們的項目已經能夠運行了。

應用中顯示的「Nothing here yet!」是由以下HTML代碼構建而成,代碼中包含了AngularJS的關鍵元素,正是咱們須要學習的。

app/index.html

<!doctype html> <html lang="en" ng-app> <head> <meta charset="utf-8"> <title>My HTML File</title> <link rel="stylesheet" href="css/app.css"> <link rel="stylesheet" href="css/bootstrap.css"> <script src="lib/angular/angular.js"></script> </head> <body> <p>Nothing here {{'yet' + '!'}}</p> </body> </html>

代碼在作什麼呢?

ng-app指令:

<html lang="en" ng-app>

ng-app指令標記了AngularJS腳本的做用域,在<html>中添加ng-app屬性即說明整個<html>都是AngularJS腳本做用域。開發者也能夠在局部使用ng-app指令,如<div ng-app>,則AngularJS腳本僅在該<div>中運行。

AngularJS腳本標籤:

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

這行代碼載入angular.js腳本,當瀏覽器將整個HTML頁面載入完畢後將會執行該angular.js腳本,angular.js腳本運行後將會尋找含有ng-app指令的HTML標籤,該標籤即定義了AngularJS應用的做用域。

雙大括號綁定的表達式:

<p>Nothing here {{'yet' + '!'}}</p>

這行代碼演示了AngularJS模板的核心功能——綁定,這個綁定由雙大括號{{}}和表達式'yet' + '!'組成。

這個綁定告訴AngularJS須要運算其中的表達式並將結果插入DOM中,接下來的步驟咱們將看到,DOM能夠隨着表達式運算結果的改變而實時更新。

AngularJS表達式Angular expression是一種相似於JavaScript的代碼片斷,AngularJS表達式僅在AngularJS的做用域中運行,而不是在整個DOM中運行。

引導AngularJS應用

經過ngApp指令來自動引導AngularJS應用是一種簡潔的方式,適合大多數狀況。在高級開發中,例如使用腳本裝載應用,您也可使用bootstrap手動引導AngularJS應用。

AngularJS應用引導過程有3個重要點:

  1. 注入器(injector)將用於建立此應用程序的依賴注入(dependency injection);
  2. 注入器將會建立根做用域做爲咱們應用模型的範圍;
  3. AngularJS將會連接根做用域中的DOM,從用ngApp標記的HTML標籤開始,逐步處理DOM中指令和綁定。

一旦AngularJS應用引導完畢,它將繼續偵聽瀏覽器的HTML觸發事件,如鼠標點擊事件、按鍵事件、HTTP傳入響應等改變DOM模型的事件。這類事件一旦發生,AngularJS將會自動檢測變化,並做出相應的處理及更新。

上面這個應用的結構很是簡單。該模板包僅含一個指令和一個靜態綁定,其中的模型也是空的。下一步咱們嘗試稍複雜的應用!

img_tutorial_00

我工做目錄中這些文件是幹什麼的?

上面的應用來自於AngularJS種子項目,咱們一般可使用AngularJS種子項目來建立新項目。種子項目包括最新的AngularJS代碼庫、測試庫、腳本和一個簡單的應用程序示例,它包含了開發一個典型的web應用程序所需的基本配置。

對於本教程,咱們對AngularJS種子項目進行了下列更改:

  1. 刪除示例應用程序;
  2. 添加手機圖像到app/img/phones/;
  3. 添加手機數據文件(JSON)到app/phones/;
  4. 添加Twitter Bootstrap文件到app/css/ 和app/img/。

練習

試試把關於數學運算的新表達式添加到index.html:

<p>1 + 2 = {{ 1 + 2 }}</p>

靜態模板

爲了說明angularJS如何加強了標準HTML,咱們先將建立一個靜態HTML頁面模板,而後把這個靜態HTML頁面模板轉換成能動態顯示的AngularJS模板。

在本步驟中,咱們往HTML頁面中添加兩個手機的基本信息,用如下命令將工做目錄重置到步驟1。

git checkout -f step-1

請編輯app/index.html文件,將下面的代碼添加到index.html文件中,而後運行該應用查看效果。

app/index.html

<ul> <li> <span>Nexus S</span> <p> Fast just got faster with Nexus S. </p> </li> <li> <span>Motorola XOOM™ with Wi-Fi</span> <p> The Next, Next Generation tablet. </p> </li> </ul>

練習

嘗試添加多個靜態HTML代碼到index.html, 例如:

<p>Total number of phones: 2</p>

AngularJS 模板

是時候給這些網頁來點動態特性了——用AngularJS!咱們這裏爲後面要加入的控制器添加了一個測試。

一個應用的代碼架構有不少種。對於AngularJS應用,咱們鼓勵使用模型-視圖-控制器(MVC)模式解耦代碼和分離關注點。考慮到這一點,咱們用AngularJS來爲咱們的應用添加一些模型、視圖和控制器。

請重置工做目錄:

git checkout -f step-2

咱們的應用如今有了一個包含三部手機的列表。

步驟1和步驟2之間最重要的不一樣在下面列出。,你能夠到GitHub去看完整的差異。

視圖和模板

在AngularJS中,一個視圖是模型經過HTML**模板**渲染以後的映射。這意味着,不論模型何時發生變化,AngularJS會實時更新結合點,隨之更新視圖。

好比,視圖組件被AngularJS用下面這個模板構建出來:

<html ng-app> <head> ... <script src="lib/angular/angular.js"></script> <script src="js/controllers.js"></script> </head> <body ng-controller="PhoneListCtrl"> <ul> <li ng-repeat="phone in phones"> {{phone.name}} <p>{{phone.snippet}}</p> </li> </ul> </body> </html>

咱們剛剛把靜態編碼的手機列表替換掉了,由於這裏咱們使用ngRepeat指令和兩個用花括號包裹起來的AngularJS表達式——{{phone.name}}{{phone.snippet}}——能達到一樣的效果。

  • <li>標籤裏面的ng-repeat="phone in phones"語句是一個AngularJS迭代器。這個迭代器告訴AngularJS用第一個<li>標籤做爲模板爲列表中的每一部手機建立一個<li>元素。
  • 正如咱們在第0步時學到的,包裹在phone.namephone.snippet周圍的花括號標識着數據綁定。和常量計算不一樣的是,這裏的表達式其實是咱們應用的一個數據模型引用,這些咱們在PhoneListCtrl控制器裏面都設置好了。

tutorial_02.png

模型和控制器

PhoneListCtrl控制器裏面初始化了數據模型(這裏只不過是一個包含了數組的函數,數組中存儲的對象是手機數據列表):

app/js/controller.js:

function PhoneListCtrl($scope) { $scope.phones = [ {"name": "Nexus S", "snippet": "Fast just got faster with Nexus S."}, {"name": "Motorola XOOM™ with Wi-Fi", "snippet": "The Next, Next Generation tablet."}, {"name": "MOTOROLA XOOM™", "snippet": "The Next, Next Generation tablet."} ]; }

儘管控制器看起來並無起到什麼控制的做用,可是它在這裏起到了相當重要的做用。經過給定咱們數據模型的語境,控制器容許咱們創建模型和視圖之間的數據綁定。咱們是這樣把表現層,數據和邏輯部件聯繫在一塊兒的:

  • PhoneListCtrl——控制器方法的名字(在JS文件controllers.js中)和<body>標籤裏面的ngController指令的值相匹配。
  • 手機的數據此時與注入到咱們控制器函數的做用域$scope)相關聯。當應用啓動以後,會有一個根做用域被建立出來,而控制器的做用域是根做用域的一個典型後繼。這個控制器的做用域對全部<body ng-controller="PhoneListCtrl">標記內部的數據綁定有效。

AngularJS的做用域理論很是重要:一個做用域能夠視做模板、模型和控制器協同工做的粘接器。AngularJS使用做用域,同時還有模板中的信息,數據模型和控制器。這些能夠幫助模型和視圖分離,可是他們二者確實是同步的!任何對於模型的更改都會即時反映在視圖上;任何在視圖上的更改都會被馬上體如今模型中。

想要更加深刻理解AngularJS的做用域,請參看AngularJS做用域文檔

測試

「AngularJS方式」讓開發時代碼測試變得十分簡單。讓咱們來瞅一眼下面這個爲控制器新添加的單元測試:

test/unit/controllersSpec.js:

describe('PhoneCat controllers', function() { describe('PhoneListCtrl', function(){ it('should create "phones" model with 3 phones', function() { var scope = {}, ctrl = new PhoneListCtrl(scope); expect(scope.phones.length).toBe(3); }); }); });

這個測試驗證了咱們的手機數組裏面有三條記錄(暫時無需弄明白這個測試腳本)。這個例子顯示出爲AngularJS的代碼建立一個單元測試是多麼的容易。正由於測試在軟件開發中是必不可少的環節,因此咱們使得在AngularJS能夠輕易地構建測試,來鼓勵開發者多寫它們。

在寫測試的時候,AngularJS的開發者傾向於使用Jasmine行爲驅動開發(BBD)框架中的語法。儘管AngularJS沒有強迫你使用Jasmine,可是咱們在教程裏面全部的測試都使用Jasmine編寫。你能夠在Jasmine的官方主頁或者Jasmine Wiki上得到相關知識。

基於AngularJS的項目被預先配置爲使用JsTestDriver來運行單元測試。你能夠像下面這樣運行測試:

  1. 在一個單獨的終端上,進入到angular-phonecat目錄而且運行./scripts/test-server.sh來啓動測試(Windows命令行下請輸入.\scripts\test-server.bat來運行腳本,後面腳本命令運行方式相似);
  2. 打開一個新的瀏覽器窗口,而且轉到http://localhost:9876 
  3. 選擇「Capture this browser in strict mode」。

    這個時候,你能夠拋開你的窗口無論而後把這事忘了。JsTestDriver會本身把測試跑完而且把結果輸出在你的終端裏。

  4. 運行./scripts/test.sh進行測試 。

    你應當看到相似於以下的結果:

    Chrome: Runner reset. . Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (2.00 ms) Chrome 19.0.1084.36 Mac OS: Run 1 tests (Passed: 1; Fails: 0; Errors 0) (2.00 ms)

    耶!測試經過了!或者沒有... 注意:若是在你運行測試以後發生了錯誤,關閉瀏覽器而後回到終端關了腳本,而後在從新來一邊上面的步驟。

練習

  • index.html添加另外一個數據綁定。例如:

    <p>Total number of phones: {{phones.length}}</p>
  • 建立一個新的數據模型屬性,而且把它綁定到模板上。例如:

    $scope.hello = "Hello, World!"

    更新你的瀏覽器,確保顯示出來「Hello, World!」

  • 用一個迭代器建立一個簡單的表:

    <table> <tr><th>row number</th></tr> <tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr> </table>

    如今讓數據模型表達式的i增長1:

    <table> <tr><th>row number</th></tr> <tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr> </table>
  • 肯定把toBe(3)改爲toBe(4)以後單元測試失敗,而後從新跑一遍./scripts/test.sh腳本

迭代器過濾

咱們在上一步作了不少基礎性的訓練,因此如今咱們能夠來作一些簡單的事情嘍。咱們要加入全文檢索功能(沒錯,這個真的很是簡單!)。同時,咱們也會寫一個端到端測試,由於一個好的端到端測試能夠幫上很大忙。它監視着你的應用,而且在發生迴歸的時候迅速報告。

請重置工做目錄:

git checkout -f step-3

咱們的應用如今有了一個搜索框。注意到頁面上的手機列表隨着用戶在搜索框中的輸入而變化。

步驟2和步驟3之間最重要的不一樣在下面列出。你能夠在GitHub裏看到完整的差異。

控制器

咱們對控制器不作任何修改。

模板

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函數來處理ngRepeat指令的輸入。

這樣容許用戶輸入一個搜索條件,馬上就能看到對電話列表的搜索結果。咱們來解釋一下新的代碼:

  • 數據綁定: 這是AngularJS的一個核心特性。當頁面加載的時候,AngularJS會根據輸入框的屬性值名字,將其與數據模型中相同名字的變量綁定在一塊兒,以確保二者的同步性。

    在這段代碼中,用戶在輸入框中輸入的數據名字稱做query,會馬上做爲列表迭代器(phone in phones | filter:query`)其過濾器的輸入。當數據模型引發迭代器輸入變化的時候,迭代器能夠高效得更新DOM將數據模型最新的狀態反映出來。

img_tutorial_03

  • 使用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端到端測試器提供的接口。

運行一個端到端測試,在瀏覽器新標籤頁中打開下面任意一個:

這個測試驗證了搜素框和迭代器被正確地集成起來。你能夠發現,在AngularJS裏寫一個端到端測試多麼的簡單。儘管這個例子僅僅是一個簡單的測試,可是用它來構建任何一個複雜、可讀的端到端測試都很容易。

練習

  • index.html模板中添加一個{{query}}綁定來實時顯示query模型的當前值,而後觀察他們是如何根據輸入框中的值而變化。
  • 如今咱們來看一下咱們怎麼讓query模型的值出如今HTML的頁面標題上。

    你或許認爲像下面這樣在title標籤上加上一個綁定就好了:

    <title>Google Phone Gallery: {{query}}</title>

    可是,當你重載頁面的時候,你根本沒辦法獲得指望的結果。這是由於query模型僅僅在body元素定義的做用域內纔有效。

    <body ng-controller="PhoneListCtrl">

    若是你想讓<title>元素綁定上query模型,你必須把ngController聲明移動HTML元素上,由於它是titlebody元素的共同祖先。

    <html ng-app ng-controller="PhoneListCtrl">

    必定要注意把body元素上的ng-controller聲明給刪了。

    當綁定兩個花括號在title元素上能夠實現咱們的目標,可是你或許發現了,頁面正加載的時候它們已經顯示給用戶看了。一個更好的解決方案是使用ngBind或者ngBindTemplate指令,它們在頁面加載時對用戶是不可見的:

    <title ng-bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
  • test/e2e/scenarios.jsdescribe塊中加入下面這些端到端測試代碼:

    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();語句,從新跑一遍。你將發現測試器暫停了!這樣容許你有機會在測試運行過程當中查看你應用的狀態。測試應用是實時的!你能夠更換搜索內容來證實。稍有經驗你就會知道,這對於在端到端測試中迅速找到問題是多麼的關鍵。

雙向綁定

在這一步你會增長一個讓用戶控制手機列表顯示順序的特性。動態排序能夠這樣實現,添加一個新的模型屬性,把它和迭代器集成起來,而後讓數據綁定完成剩下的事情。

請重置工做目錄:

git checkout -f step-4

你應該發現除了搜索框以外,你的應用多了一個下來菜單,它能夠容許控制電話排列的順序。

步驟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>標籤,這樣咱們的用戶就能夠選擇咱們提供的兩種排序方法。

img_tutorial_04

  • 而後,在filter過濾器後面添加一個orderBy過濾器用其來處理進入迭代器的數據。orderBy過濾器以一個數組做爲輸入,複製一份副本,而後把副本重排序再輸出到迭代器。

AngularJS在select元素和orderProp模型之間建立了一個雙向綁定。然後,orderProp會被用做orderBy過濾器的輸入。

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

    如今咱們該好好談談雙向數據綁定了。注意到當應用在瀏覽器中加載時,「Newest」在下拉菜單中被選中。這是由於咱們在控制器中把orderProp設置成了‘age’。因此綁定在從咱們模型到用戶界面的方向上起做用——即數據從模型到視圖的綁定。如今當你在下拉菜單中選擇「Alphabetically」,數據模型會被同時更新,而且手機列表數組會被從新排序。這個時候數據綁定從另外一個方向產生了做用——即數據從視圖到模型的綁定。

測試

相關文章
相關標籤/搜索