template(模板):帶有附加標記的模板HTML
directives(指令):使用自定義屬性和元素擴展HTML
model(模型):用戶在視圖中顯示的數據,並與用戶進行交互
scope(做用域):存儲模型的上下文,以便控制器,指令和表達式能夠訪問它
expressions(表達式):訪問範圍中的變量和函數
compiler(編譯器):解析模板並實例化指令和表達式
filter(過濾器):格式化表達式的值以顯示給用戶
view(視圖):用戶看到的內容(DOM)
Data Binding(數據綁定):在模型和視圖之間同步數據
controller(控制器):視圖後面的業務邏輯
Dependency Injection:建立並鏈接對象和函數
injector(注入器):依賴注入容器
module(模塊):一個用於應用程序不一樣部分的容器,包括控制器,服務,過濾器,配置Injector的指令
service (服務):可重用的業務邏輯獨立於視圖
第一個例子:數據綁定
在下面的示例中,咱們將構建一個表單來計算不一樣貨幣的發票成本。
讓咱們從數量和成本的輸入字段開始,其值相乘以產生髮票總額:html
<div ng-app ng-init="qty=1;cost=2"> <b>Invoice:</b> <div> Quantity: <input type="number" min="0" ng-model="qty"> </div> <div> Costs: <input type="number" min="0" ng-model="cost"> </div> <div> <b>Total:</b> {{qty * cost | currency}} </div> </div>
這看起來像正常的HTML,有一些新的標記。 在AngularJS中,像這樣的文件稱爲模板。 當AngularJS啓動你的應用程序時,它使用編譯器從模板解析和處理這個新的標記。 加載,轉換和渲染的DOM而後稱爲視圖。
第一種新的標記是指令。 它們對HTML中的屬性或元素應用特殊的行爲。 在上面的例子中,咱們使用ng-app屬性,它連接到一個自動初始化咱們的應用程序的指令。 AngularJS還定義了一個爲元素添加額外行爲的輸入元素的指令。 ng-model指令存儲/更新輸入字段的值到/自變量。
訪問DOM的自定義指令:在AngularJS中,應用程序應該訪問DOM的惟一位置是指令內。 這很重要,由於訪問DOM的工件難以測試。 若是你須要直接訪問DOM,你應該爲它寫一個自定義指令。
第二種新的標記是雙花括號{{expression | filter}}:當編譯器遇到這個標記時,它將用標記的估計值替換它。 模板中的表達式是一個相似JavaScript的代碼片斷,它容許AngularJS讀取和寫入變量。 請注意,這些變量不是全局變量。 就像JavaScript函數中的變量存在於做用域中同樣,AngularJS爲表達式可訪問的變量提供了一個做用域。 存儲在做用域上的變量中的值在文檔的其他部分中稱爲模型。 應用於上面的示例,標記指示AngularJS「獲取從輸入小部件獲取的數據並將它們相乘」。
上面的示例還包含一個過濾器。 過濾器格式化表達式的值以顯示給用戶。 在上面的示例中,過濾器貨幣將數字格式化爲看起來像金錢的輸出。
在示例中重要的是,AngularJS提供活動綁定:每當輸入值更改時,表達式的值都會自動從新計算,並使用其值更新DOM。 這背後的概念是雙向數據綁定。
添加UI邏輯:控制器express
invoice1.jsjson
angular.module('invoice1', []) .controller('InvoiceController', function InvoiceController() { this.qty = 1; this.cost = 2; this.inCurr = 'EUR'; this.currencies = ['USD', 'EUR', 'CNY']; this.usdToForeignRates = { USD: 1, EUR: 0.74, CNY: 6.09 }; this.total = function total(outCurr) { return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr); }; this.convertCurrency = function convertCurrency(amount, inCurr, outCurr) { return amount * this.usdToForeignRates[outCurr] / this.usdToForeignRates[inCurr]; }; this.pay = function pay() { window.alert('Thanks!'); }; });
index.html後端
<div ng-app="invoice1" ng-controller="InvoiceController as invoice"> <b>Invoice:</b> <div> Quantity: <input type="number" min="0" ng-model="invoice.qty" required > </div> <div> Costs: <input type="number" min="0" ng-model="invoice.cost" required> <select ng-model="invoice.inCurr"> <option ng-repeat="c in invoice.currencies">{{c}}</option> </select> </div> <div> <b>Total:</b> <span ng-repeat="c in invoice.currencies"> {{invoice.total(c) | currency:c}} </span><br> <button class="btn" ng-click="invoice.pay()">Pay</button> </div> </div>
首先,有一個包含控制器的新JavaScript文件。更準確地說,該文件指定將用於建立實際控制器實例的構造函數。控制器的目的是將變量和功能暴露給表達式和指令。
除了包含控制器代碼的新文件,咱們還向HTML添加了ng-controller指令。這個指令告訴AngularJS,新的InvoiceController負責具備指令的元素和全部元素的子元素。語法InvoiceController做爲憑證告訴AngularJS實例化控制器並將其保存在當前做用域中的變量invoice 中。
咱們還更改了頁面中的全部表達式,以便在控制器實例中讀取和寫入變量,方法是在它們前面加上invoice 。 。可能的貨幣在控制器中定義,並使用ng-repeat添加到模板。因爲控制器包含一個總函數,咱們還可使用{{invoice.total(...)}}將該函數的結果綁定到DOM。
再次,這種綁定是活的,即,當函數的結果改變時,DOM將被自動更新。用於支付發票的按鈕使用指令ngClick。這將在每次點擊按鈕時評估相應的表達式。
在新的JavaScript文件中,咱們還建立了一個模塊,咱們在其中註冊控制器。咱們將在下一節中討論模塊。
下圖顯示了在咱們介紹控制器以後,一切是如何協同工做的:
設計模式
獨立於視圖的業務邏輯:服務service
如今,InvoiceController包含咱們示例的全部邏輯。 當應用程序增加時,將與視圖無關的邏輯從控制器移動到服務中是一個好的作法,所以它也能夠由應用程序的其餘部分重用。 稍後,咱們還能夠更改該服務以從網絡(例如網絡)載入匯率。 經過調用Yahoo Finance API,而無需更改控制器。
讓咱們重構咱們的例子,並將貨幣轉換轉換爲另外一個文件中的服務:api
finance2.js數組
angular.module('finance2', []) .factory('currencyConverter', function() { var currencies = ['USD', 'EUR', 'CNY']; var usdToForeignRates = { USD: 1, EUR: 0.74, CNY: 6.09 }; var convert = function(amount, inCurr, outCurr) { return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr]; }; return { currencies: currencies, convert: convert }; });
invoice2.js服務器
angular.module('invoice2', ['finance2']) .controller('InvoiceController', ['currencyConverter', function InvoiceController(currencyConverter) { this.qty = 1; this.cost = 2; this.inCurr = 'EUR'; this.currencies = currencyConverter.currencies; this.total = function total(outCurr) { return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr); }; this.pay = function pay() { window.alert('Thanks!'); }; }]);
index.html網絡
<div ng-app="invoice2" ng-controller="InvoiceController as invoice"> <b>Invoice:</b> <div> Quantity: <input type="number" min="0" ng-model="invoice.qty" required > </div> <div> Costs: <input type="number" min="0" ng-model="invoice.cost" required > <select ng-model="invoice.inCurr"> <option ng-repeat="c in invoice.currencies">{{c}}</option> </select> </div> <div> <b>Total:</b> <span ng-repeat="c in invoice.currencies"> {{invoice.total(c) | currency:c}} </span><br> <button class="btn" ng-click="invoice.pay()">Pay</button> </div> </div>
咱們將convertCurrency函數和現有貨幣的定義移動到新文件finance2.js中。可是控制器如何保持如今分離的功能?
這是依賴注入發揮做用的地方。依賴注入(DI)是一種軟件設計模式,它處理對象和函數如何建立以及如何得到它們的依賴。 AngularJS中的全部內容(指令,過濾器,控制器,服務,...)都是使用依賴注入建立和鏈接的。在AngularJS中,DI容器稱爲注入器。
要使用DI,須要一個地方,全部應該一塊兒工做的東西都註冊。在AngularJS中,這是模塊的目的。當AngularJS啓動時,它將使用具備由ng-app指令定義的名稱的模塊配置,包括該模塊所依賴的全部模塊的配置。
在上面的示例中:模板包含指令ng-app =「invoice2」。這告訴AngularJS使用invoice2模塊做爲應用程序的主模塊。代碼片斷angular.module('invoice2',['finance2'])指定invoice2模塊依賴於finance2模塊。經過這個,AngularJS使用InvoiceController以及currencyConverter服務。
如今AngularJS知道應用程序的全部部分,它須要建立它們。在上一節中,咱們看到控制器是使用構造函數建立的。對於服務,有多種方式指定如何建立它們(請參閱服務指南)。在上面的例子中,咱們使用一個匿名函數做爲currencyConverter服務的工廠函數。此函數應返回currencyConverter服務實例。
回到最初的問題:InvoiceController如何得到對currencyConverter函數的引用?在AngularJS中,這是經過簡單地定義參數的構造函數。這樣,注入器可以以正確的順序建立對象,而且將先前建立的對象傳遞到依賴於它們的對象的工廠中。在咱們的示例中,InvoiceController有一個名爲currencyConverter的參數。經過這個,AngularJS知道控制器和服務之間的依賴關係,並以服務實例做爲參數來調用控制器。
在上一節和本節之間的示例中,最後一件事情是改變了,如今咱們將一個數組傳遞給module.controller函數,而不是一個簡單的函數。數組首先包含控制器須要的服務依賴關係的名稱。數組中的最後一個條目是控制器構造函數。 AngularJS使用這個數組語法來定義依賴關係,以便DI也能夠在縮小代碼後工做,這極可能會將控制器構造函數的參數名稱從新命名爲更短的。
app
訪問後端
讓咱們經過從Yahoo Finance API獲取匯率來完成咱們的示例。 如下示例說明如何使用AngularJS完成此操做:
invoice3
angular.module('invoice3', ['finance3']) .controller('InvoiceController', ['currencyConverter', function InvoiceController(currencyConverter) { this.qty = 1; this.cost = 2; this.inCurr = 'EUR'; this.currencies = currencyConverter.currencies; this.total = function total(outCurr) { return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr); }; this.pay = function pay() { window.alert('Thanks!'); }; }]);
finance3.js
angular.module('finance3', []) .factory('currencyConverter', ['$http', function($http) { var YAHOO_FINANCE_URL_PATTERN = '//query.yahooapis.com/v1/public/yql?q=select * from ' + 'yahoo.finance.xchange where pair in ("PAIRS")&format=json&' + 'env=store://datatables.org/alltableswithkeys'; var currencies = ['USD', 'EUR', 'CNY']; var usdToForeignRates = {}; var convert = function(amount, inCurr, outCurr) { return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr]; }; var refresh = function() { var url = YAHOO_FINANCE_URL_PATTERN. replace('PAIRS', 'USD' + currencies.join('","USD')); return $http.get(url).then(function(response) { var newUsdToForeignRates = {}; angular.forEach(response.data.query.results.rate, function(rate) { var currency = rate.id.substring(3,6); newUsdToForeignRates[currency] = window.parseFloat(rate.Rate); }); usdToForeignRates = newUsdToForeignRates; }); }; refresh(); return { currencies: currencies, convert: convert }; }]);
index.html
<div ng-app="invoice3" ng-controller="InvoiceController as invoice"> <b>Invoice:</b> <div> Quantity: <input type="number" min="0" ng-model="invoice.qty" required > </div> <div> Costs: <input type="number" min="0" ng-model="invoice.cost" required > <select ng-model="invoice.inCurr"> <option ng-repeat="c in invoice.currencies">{{c}}</option> </select> </div> <div> <b>Total:</b> <span ng-repeat="c in invoice.currencies"> {{invoice.total(c) | currency:c}} </span><br> <button class="btn" ng-click="invoice.pay()">Pay</button> </div> </div>
咱們的financeConverter服務的財務模塊如今使用$ http,AngularJS提供的內置服務訪問服務器後端。 $ http是XMLHttpRequest和JSONP傳輸的包裝器。