<!doctype html>
<html lang="en" ng-app>
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Demo</title>
<script src="http://code.angularjs.org/1.2.25/angular.min.js"></script>
</head>
<body>
<div ng-init="qty=1;cost=2">
<b>订单:</b>
<div>
数量: <input type="number" ng-model="qty" required>
</div>
<div>
单价: <input type="number" ng-model="cost" required>
</div>
<div>
<b>总价:</b>
</div>
</div>
</body>
</html>
这看起来很像标准的HTML,只是带了一些新的标记。在AngularJS中,像这样的文件叫做"模板(template)"。当AngularJS启动你的应用时,它通过"编译器(compiler)"来解析并处理模板中的这些标记。这些经过加载,转化,渲染而成的DOM就叫做"视图(View)".
第一类新标记叫做"指令(directive)"。它们通过HTML中的属性或元素来为页面添加特定的行为。np-app属性,与此相关的指令(directive)负责自动初始化我们的应用程序。还为input元素定义了一个指令,它负责添加额外的行为到这个元素上。例如,当它发现了input元素的required属性,就会自动进行验证,确保所输入的文本不是空白。
ng-model指令则负责从变量(比如这里的qty,cost)加载input元素的value值,并且把input元素的value值写回变量中。并且,还会根据对input元素进行校验的结果自动添加相应的css类。
第二类新标记是双大括号{{expression|filter}}。当编译器遇到这种标记时,就会把这些标记替换为这个表达式计算的结果。AngularJS为这些能从表达式中访问的变量提供了一个"作用域(scope)"。这些存储于AngularJS作用域(Scope)的变量叫做Scope变量,这些变量所代表的数据叫做"模型(model)"。
AngularJS提供了动态(live)的绑定:当input元素的值变化的时候,表达式的值也会自动重新计算,并且DOM所呈现的内容也会随着这些值得变化而自动更新。这种模型(model)与视图(view)的联动就叫做"双向数据绑定"。
angular.module('invoice1',[])
.controller('InvoiceController', function() {
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(outCur) {
return this.convertCurrency(this.qty * this.cost, this.inCurr, outCur);
};
this.convertCurrency = function covertCurrency(amount, inCurr, outCurr) {
return amout * this.usdToForeignRates[outCurr]*1/this.usdToForeignRates[inCurr];
};
this.pay = function pay() {
window.alert("谢谢!");
}
})
<pre name="code" class="html"><!doctype html>
<html lang="en" ng-app="invoice1">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Demo</title>
<script src="angular.min.js"></script>
<script src="invoice1.js"></script>
</head>
<body>
<div ng-controller="InvoiceController as invoice">
<b>订单:</b>
<div>
数量: <input type="number" ng-model="invoice.qty" required>
</div>
<div>
单价: <input type="number" ng-model="invoice.cost" required>
<select ng-model="invoice.inCurr">
<option ng-repeat="c in invoice.currencies"></option>
</select>
</div>
<div>
<b>总价:</b>
<span ng-repeat="c in invoice.currencies">
</span>
<button class="btn" ng-click="invoice.pay()">支付</button>
</div>
</div>
</body>
</html>
这个文件中定义了一个构造函数,它用来在将来真正需要的时候创建这个控制器函数的实例。控制器的用途是导出一些变量和函数,供模板中的表达式和指令使用。
在创建一个控制器的同时,还往HTML中添加了一个ng-controller指令。这个指令告诉AngularJS,我们创建的这个InvoiceController控制器将会负责管理这个带有ng-controller指令的div节点,及其各级子节点。InvoiceController as invoice 告诉AngularJS:创建这个InvoiceController的实例,并且把这个实例赋值给当前作用域(Scope)中的invoice变量。同时,修改了页面所有用于读写Scope变量的表达式,给它们加上一个invoice.的前缀。把选的币种作为一个数组定义在控制器中,并且通过ng-repeat指令把它们添加到模板。控制器中还包含了一个total函数,我们也能在DOM中使用{{invoice.total(...)}}表达式绑定总价的计算结果。
这个绑定是动态的,也就是说:当invoice.total函数的返回值变化的时候,DOM也会自动更新。
把控制器中与视图无关的逻辑都移动“服务(service)”中。以便这个应用程序的其他部分也能服用这些逻辑。
angular.module('finance2',[])
.factory('currencyConverter', function() {
var currencies = ['USD', 'EUR', 'CNY'],
usdToForeignRates = {
USD: 1,
EUR: 0.74,
CNY: 6.09
};
return {
currencies: currencies,
convert:convert
};
function convert(amount, inCurr, outCurr) {
return amount * usdToForeignRates[outCurr] * 1 / usdToForeignRates[inCurr];
}
});
angular.module('invoice2', ['finance2'])
.controller('InvoiceController', ['currencyConverter', function(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.pay("谢谢!");
}
}])
我们把convertCurrency函数和所支持的币种的定义独立到一个新的文件:finance.js。控制器怎样才能找到这个独立的函数呢?
依赖注入(DI)是一种设计模式,它用于解决下列问题:我们创建了对象和函数,但是怎么得到自己所依赖的对象呢?AngularJS中每一样东西都是用依赖注入的方式来创建和使用的,比如指令(Directive),过滤器(Filter),控制器(Controller),服务(Service)。在AngularJS中,依赖注入的容器叫做"注入器(injector)"。
要想进行依赖注入,必须先把这些需要协同工作的对象和函数注册到某个地方。在AngularJS中,这个地方叫做"模块(module)"。
模板中包了一个ng-app="invoice2"指令。这告诉AngularJS使用invoice2模块作为该程序的主模块。像angular.module('invoice', [finance''])这样的代码告诉Angular:invoice模块依赖于finance模块。这样,Angular就能同时使用InvoiceController这个控制器和currencyConverter这个服务了。
把一个数组作为参数传入到module.controller函数中,而不是一个普通的函数。这个数组前面部分的元素包含这个控制器所依赖的一系列服务的名字,最后一个元素则是这个控制器的构造函数。Angular通过这种数组语法来定义依赖。