所有angularjs笔记均参考自
AngularJS是一个开发动态Web应用的JavaScript框架。它让你可以使用HTML作为模板语言并且可以通过扩展的HTML语法来使应用组件更加清晰和简洁。它的创新之处在于,通过数据绑定和依赖注入减少了大量代码,而这些都在浏览器端通过JavaScript实现,能够和任何服务器端技术完美结合。
Angular通过指令(directive)扩展HTML的语法。例如:
下面是Angular的一些出众之处:
AngularJS 应用组成如下:
AngularJS 是以一个 JavaScript 文件形式发布的,可通过 script
标签添加到网页中,来导入 Angular 框架:
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js">script>
script
标签。这样可以优化应用的加载时间,因为避免了HTML加载时被 angular.js
脚本的加载阻滞。你可以在http://code.angularjs.org获取 Angular 的最新版本。注意,别在你的产品代码中连接这个 URL,因为这样会在你的网站中暴露一个安全问题。当然,在开发的时候连接是没有问题的。angular-[version].js
这样一个可读的版本,用来作为开发和调试用angular-[version].min.js
这样一个压缩版本,用来作为产品发布时用ng-app
放在应用的根结点中,通常情况下是
标签中下表概述一些 Angular 的主要概念:
概念 | 说明 |
---|---|
模板(Template) | 带有Angular扩展标记的HTML |
指令(Directive) | 用于通过自定义属性和元素扩展HTML的行为 |
模型(Model) | 用于显示给用户并且与用户互动的数据 |
作用域(Scope) | 用来存储模型(Model)的语境(context)。模型放在这个语境中才能被控制器、指令和表达式等访问到 |
表达式(Expression) | 模板中可以通过它来访问作用域(Scope)中的变量和函数 |
编译器(Compiler) | 用来编译模板(Template),并且对其中包含的指令(Directive)和表达式(Expression)进行实例化 |
过滤器(Filter) | 负责格式化表达式(Expression)的值,以便呈现给用户 |
视图(View) | 用户看到的内容(即DOM) |
数据绑定(Data Binding) | 自动同步模型(Model)中的数据和视图(View)表现 |
控制器(Controller) | 视图(View)背后的业务逻辑 |
依赖注入(Dependency Injection) | 负责创建和自动装载对象或函数 |
注入器(Injector) | 用来实现依赖注入(Injection)的容器 |
模块(Module) | 用来配置注入器 |
服务(Service) | 独立于视图(View)的、可复用的业务逻辑 |
下面我们将编写一个表单,用来计算一个订单在不同币种下的总价。
首先,我们做一个单币种表单,它有数量和单价两个输入框,并且把数量和单价相乘得出该订单的总价。
<html ng-app>
<head>
<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>总价:{{qty * cost | currency}}b>
div>
div>
body>
html>
在Angular中,像这样的文件叫做“模板(template)”。 当Angular启动你的应用时,它通过“编译器(compiler)”来解析并处理模板中的这些新标记。 这些经过加载、转换、渲染而成的DOM就叫做“视图(view)”
第一类新标记叫做“指令(directive)”。 它们通过HTML中的属性或元素来为页面添加特定的行为。上面的例子中,我们使用 ng-app
指令自动初始化了我们的应用程序。ng-model
指令则负责从变量(比如这里的qty、cost等)加载input
元素的value
值,并且把input
元素的value
值写回变量中。
第二类新标记是双大括号{{ expression | filter }}
,其中expression是“表达式”语句,filter是“过滤器”语句。 当编译器遇到这种标记时,它会把这些标记替换为这个表达式的计算结果。 模板中的”表达式”是一种类似于*JavaScript的代码片段,它允许你读写变量。注意,表达式中所用的变量并不是全局变量。 就像JavaScript函数定义中的变量都属于某个作用域一样,Angular也为这些能从表达式中访问的变量提供了一个“作用域(scope)*”。 这些存储于Angular作用域(Scope)中的变量叫做Scope变量,这些变量所代表的数据叫做“模型(model)”。 在上面的例子中,这些标记告诉Angular:“从这两个input
元素中获取数据,并把它们乘在一起。”
上面这个例子中还包含一个”过滤器(filter)“。 过滤器格式化表达式的值,以便呈现给用户。 上面的例子中currency
过滤器把一个数字格式化为金额的形式进行输出。
这个例子中最重要的一点是:Angular提供了动态(live)的绑定: 当input
元素的值变化的时候,表达式的值也会自动重新计算,并且DOM所呈现的内容也会随着这些值的变化而自动更新。 这种模型(model)与视图(view)的联动就叫做“双向数据绑定”。
现在,我们添加一些逻辑,以便让这个例子支持不同的币种。它将允许我们使用不同的币种来输入、计算和支付这个订单。
<html ng-app="invoice1">
<head>
<script src="http://code.angularjs.org/1.2.25/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>
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(outCurr) {
return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr);
};
this.convertCurrency = function convertCurrency(amount, inCurr, outCurr) {
return amount * this.usdToForeignRates[outCurr] * 1 / this.usdToForeignRates[inCurr];
};
this.pay = function pay() {
window.alert("thanks");
};
});
首先,出现了一个新的JavaScript文件,它包含一个被称为”控制器(controller)“的函数。 更确切点说:这个文件中定义了一个构造函数,它用来在将来真正需要的时候创建这个控制器函数的实例。 控制器的用途是导出一些变量和函数,供模板中的表达式(expression)和指令(directive)使用。
在创建一个控制器的同时,我们还往HTML中添加了一个ng-controller
指令。 这个指令告诉Angular,我们创建的这个InvoiceController
控制器将会负责管理这个带有ng-controller
指令的div节点,及其各级子节点。InvoiceController as invoice
这个语法告诉Angular:创建这个InvoiceController
的实例,并且把这个实例赋值给当前作用域(Scope)中的invoice
变量。
同时,我们修改了页面中所有用于读写Scope变量的表达式,给它们加上了一个invoice.
前缀。 我们还把可选的币种作为一个数组定义在控制器中,并且通过ng-repeat
指令把它们添加到模板。 由于控制器中还包含了一个total
函数,我们也能在DOM中使用{{ invoice.total(...) }}
表达式来绑定总价的计算结果。
同样,这个绑定也是动态(live)的,也就是说:当invoice.total函数的返回值变化的时候,DOM也会跟着自动更新。 表单中的“支付”按钮附加上了指令ngClick
。 这意味着当它被点击时,会自动执行invoice.pay()
这个表达式,即:调用当前作用域中的pay函数。
在这个新的JavaScript文件中,我们还创建了一个**模块(module)**
,并且在这个模块中注册了控制器(controller)。
现在,InvoiceController
包含了我们这个例子中的所有逻辑。如果这个应用程序的规模继续成长,最好的做法是:把控制器中与视图无关的逻辑都移到”服务(service)“中。 以便这个应用程序的其他部分也能复用这些逻辑。
接下来,就让我们重构我们的例子,并且把币种兑换的逻辑移入到一个独立的服务(service)中。
<html ng-app="invoice2">
<head>
<script src="http://code.angularjs.org/1.2.25/angular.min.js">script>
<script src="finance2.js">script>
<script src="invoice2.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>
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.alert("thanks");
};
}]);
我们把convertCurrency
函数和所支持的币种的定义独立到一个新的文件:finance.js
。但是控制器怎样才能找到这个独立的函数呢?
这下该“依赖注入(Dependency Injection)”出场了。依赖注入(DI)是一种设计模式(Design Pattern),它用于解决下列问题:我们创建了对象和函数,但是它们怎么得到自己所依赖的对象呢? Angular中的每一样东西都是用依赖注入(DI)的方式来创建和使用的,比如指令(Directive)、过滤器(Filter)、控制器(Controller)、服务(Service)。 在Angular中,依赖注入(DI)的容器(container)叫做”注入器(injector)“。
要想进行依赖注入,你必须先把这些需要协同工作的对象和函数注册(Register)到某个地方。在Angular中,这个地方叫做“模块(module)”。
在上面这个例子中: 模板包含了一个ng-app="invoice2"
指令。这告诉Angular使用invoice2
模块作为该应用程序的主模块。 像angular.module('invoice', ['finance'])
这样的代码告诉Angular:invoice
模块依赖于finance
模块。 这样一来,Angular就能同时使用InvoiceController
这个控制器和currencyConverter
这个服务了。
刚才我们已经定义了应用程序的所有部分,现在,需要Angular来创建它们了。 在上一节,我们了解到控制器(controller)是通过一个工厂函数创建的。 而对于服务(service),则有多种方式来定义它们的工厂函数。 上面这个例子中,我们通过一个返回currencyConverter
函数的函数作为创建currencyConverter
服务的工厂。 (译注:js中的“工厂(factory)”是指一个以函数作为“返回值”的函数)
回到刚才的问题:InvoiceController
该怎样获得这个currencyConverter
函数的引用呢? 在Angular中,这非常简单,只要在构造函数中定义一些具有特定名字的参数就可以了。 这时,注入器(injector)就可以按照正确的依赖关系创建这些对象,并且根据名字把它们传入那些依赖它们的对象工厂中。 在我们的例子中,InvoiceController
有一个叫做currencyConverter
的参数。 根据这个参数,Angular就知道InvoiceController
依赖于currencyConverter
,取得currencyConverter
服务的实例,并且把它作为参数传给InvoiceController
的构造函数。
这次改动中的最后一点是我们现在把一个数组作为参数传入到module.controller
函数中,而不再是一个普通函数。 这个数组前面部分的元素包含这个控制器所依赖的一系列服务的名字,最后一个元素则是这个控制器的构造函数。 Angular可以通过这种数组语法来定义依赖,以免js代码压缩器(Minifier)破坏这个“依赖注入”的过程。 因为这些js代码压缩器通常都会把构造函数的参数重命名为很短的名字,比如a
,而常规的依赖注入是需要根据参数名来查找“被注入对象”的。