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传输的包装器。