這部分通過示例簡略的介紹了Angular的所有重要內(nèi)容, 詳情見向?qū)Р糠?
第一個示例: 數(shù)據(jù)綁定
在下面的例子中, 我們將創(chuàng)建一個表單來計算訂單的金額.首先輸入數(shù)量和單價, 它們的乘積就是訂單的總金額:index.html
試著運行上面的實時預(yù)覽, 然后我們將通讀示例源碼, 描述它是怎樣運行的.
這看起來像是普通的HTML, 只是多了些新的標(biāo)記. 在Angular中, 像這樣一個文件被叫做"template(模板)". Angular啟動應(yīng)用時, 它會使用"compiler(編譯器)"解析并且處理模板中的新標(biāo)記. 這些被加載, 轉(zhuǎn)換和渲染的DOM就是"view(視圖)".
第一種新的標(biāo)記叫做"directives(指令)". 指令適用于在HTML屬性或元素中添加特定的行為. 上面示例中, 我們使用了指令ng-app屬性, 它的作用就是初始化我們的應(yīng)用. Angular也為input元素定義了指令, 通過這個指令可以為元素添加額外的行為. 通過ng-model指令可以將輸入框中的值存儲到變量中, 或?qū)⒆兞恐械闹蹈碌捷斎肟蛑?
通過自定義指令訪問DOM:在Angular中, 應(yīng)用唯一一個可以訪問DOM的地方就是在指令內(nèi)部. 這一點非常重要, 因為需要訪問DOM的實現(xiàn)是非常難以測試的. 如果你真的需要直接訪問DOM, 那么你應(yīng)該創(chuàng)建一個自定義指令來完成訪問DOM的操作. 指令指南中介紹了如何創(chuàng)建自定義指令.
第二種新的標(biāo)記是雙花括號{{ expression | filter }}: 當(dāng)編譯器遇到這種標(biāo)記時, 它會用實際的值去替換標(biāo)記本身. 模板中的"expression(表達(dá)式)"就像是Javascript代碼片段, 它可以對變量進(jìn)行讀寫操作. 需要注意的是, 那些變量并不是全局變量. 就在定義在Javascript函數(shù)中的變量屬于某一個作用域一樣, Angular也為表達(dá)式中訪問的變量提供了一個"scope(作用域)". 這些存儲在作用域變量中的值被稱為"model(模型)". 在上面的示例中, 這些告訴Angular: "從輸入框中取得數(shù)據(jù), 并將它們相乘".
上面的示例中包含一個"filter(過濾器)". 過濾器可以將表達(dá)式的值格式化后展示給用戶. 在上面的示例中, 過濾器currency將一個數(shù)字格式化為貨幣的格式進(jìn)行輸出.
重要的一點是, 在上面的示例中, Angular提供的動態(tài)綁定機(jī)制: 輸入的值無論什么時候改變, 表達(dá)式的值都會自動重新計算, DOM元素也會自動更新顯示的值. 這就是Angular所提供的模型與視圖之間的"雙向數(shù)據(jù)綁定機(jī)制".
添加UI邏輯: 控制器
現(xiàn)在讓我們在上面的示例中添加一些業(yè)務(wù)邏輯, 使我們可以使用不同的貨幣輸入, 計算金額并完成支付.
? ? ? ?invoice.js
index.html
做了哪些修改 ?
首先, 添加了一個JavaScript文件, 其中有個被叫做"controller(控制器)"的函數(shù). 更確切的說, 這個文件中包含一個構(gòu)造函數(shù), 這個構(gòu)造函數(shù)可以創(chuàng)建控制器實例. 控制器的作用是給表達(dá)式和指令暴露變量和函數(shù), 供它們使用.
除這個包含控制器代碼的JavaScript文件以外, 我們還在HTML中添加了ng-controller指令. 這個指令告訴Angular, 這個新的控制器InvoiceController會負(fù)責(zé)管理包含它的元素及其所有子元素.InvoiceController as invoice這種語法告訴Angular要初始化這個控制器, 并且將它賦值給當(dāng)前作用域的變量invoice.
我們還修改了頁面中讀寫變更的所有表達(dá)式, 給他們添加了控制器的實例名稱invoice.作為前綴. 我們在控制器中定義一些貨幣的各類, 并且通過指令ng-repeat把它們添加到模板中. 由于控制器還還有個total函數(shù), 我們還能將它的結(jié)果值通過表達(dá)式{{ invoice.total(...) }}綁定在DOM元素上.
當(dāng)然, 這個綁定也是動態(tài)的, 換句話說, 無論函數(shù)結(jié)果什么時候發(fā)生變化, DOM都會自動修改其展示的值. 付款按鈕使用了指令ngClick, 無論什么時候點擊這個按鈕, 它將計算相應(yīng)表達(dá)式的值.
在這個新增的JavaScript文件中, 我們還創(chuàng)建一個module(模塊), 并且將控制器注冊在了這個模塊上. 我們將在下一部分對模塊進(jìn)行討論.
下圖展示的是加入了控制器之后, 應(yīng)用中的每一部分是如何協(xié)作的:
獨立于視圖的業(yè)務(wù)邏輯: Services(服務(wù))
現(xiàn)在, 控制器InvoiceController包含了示例中的所有邏輯. 當(dāng)這個應(yīng)用繼續(xù)擴(kuò)展, 那么最好的作法是將與視圖無關(guān)的業(yè)務(wù)邏輯從控制器中移動至"service(服務(wù))"中, 這樣它都能更好的被應(yīng)用的其他部分重用. 日后, 我們也可以修改腳本, 將它改成從網(wǎng)絡(luò)中加載匯率, 例如Yahoo的金融API, 而不會修改控制器.
我們重構(gòu)下我們的示例, 將貨幣轉(zhuǎn)換移動到另一個文件的service(服務(wù))中.
finance2.js
invoice2.js
index.html
有哪些變化? 我們將函數(shù)convertCUrrency和已經(jīng)定義好的貨幣變量移動到finance2.js中. 但是控制器是怎樣引用已經(jīng)分離出去的函數(shù)的呢 ?
輪到"Dependency Injection(依賴注入)"出場了. Dependency Injection (DI)依賴注入是一種軟件設(shè)計模式, 它解決了對象和函數(shù)是如何得到已經(jīng)創(chuàng)建好的并且被它們所依賴的對象的引用的. Angular中的每個部分(指令, 過濾器, 控制器, 服務(wù)...)都是通過依賴注入機(jī)制來創(chuàng)建和關(guān)聯(lián)的. 在Angular中, DI窗口被稱叫"injector".
為了使用DI(依賴注入), 所有需要協(xié)作的部件都需要統(tǒng)一注冊的一個位置. 在Angular中, "mudules(模塊)"就是為解決這個問題. Angular從指令ng-app開始啟動, 它會根據(jù)模塊名稱加載模塊配置, 包括此模塊所依賴的所有模塊.
在上面的示例中: 模板中包含了指令ng-app="invoice2". Angular就會使用模塊invoice2作為整個應(yīng)用的主模塊. 代碼段angular.module('invoice2', ['finance2'])指定了模塊invoice2依賴于模塊finance2. 這樣, Angular就即可以使用控制器InvoiceController以及服務(wù)currencyConverter.
既然Angular了解應(yīng)用所有部分的定義, 那么就可以來創(chuàng)建它們. 在上一段中, 我們是使用一個工廠方法創(chuàng)建了控制器. 而對于服務(wù)來說, 有多種定義它們工廠的方式(見服務(wù)指南). 在上面的示例中, 我使用一個返回currencyConverter函數(shù)的函數(shù)作為創(chuàng)建currencyConverter服務(wù)的工廠.
回到最初的問題: 控制器InvoiceController是怎樣獲得函數(shù)currencyConverter的引用的? 在Angular中, 通過定義構(gòu)造函數(shù)的參數(shù)就可以做到這一點. 通過這種方式, 注入器(injector)根據(jù)正確的依賴順序創(chuàng)建這些對象, 并將創(chuàng)建好的對象傳入依賴它們的工廠中. 在我們的示例中, 控制器InvoiceController的構(gòu)造函數(shù)有一個命名為currencyConverter的參數(shù). 通過這個參數(shù), Angular便知道控制器與服務(wù)之間的依賴關(guān)系, 并且把服務(wù)對象作為參數(shù)來調(diào)用控制器的構(gòu)造函數(shù).
這次改動中的最后一點是, 我們將一個數(shù)組傳入了module.controller函數(shù)中, 而不再是一個普通函數(shù). 首先, 這個數(shù)組包含了控制器所依賴的服務(wù)的名稱. 數(shù)組的最后一個元素則是控制器的構(gòu)造函數(shù). Angular通過這種數(shù)組的語法定義依賴關(guān)系, 使得依賴注入發(fā)生在壓縮代碼之后, 因為代碼壓縮通常都會將控制器構(gòu)造函數(shù)的名稱重命名為很短的名稱, 比如a.
訪問后臺
讓我們通從Yahoo金融API獲取匯率來完成我們的示例. 下面的示例將展示Angular是怎樣做的:
invoice3.js
finance3.js
index.html
修改了什么?finance模塊的currencyConverter服務(wù)使用了$http服務(wù), 一個Angular內(nèi)置的服務(wù), 使用它可以訪問后臺服務(wù)器.$http就是封裝了XMLHttpRequest和JSONP傳輸.