1. 文章简述
这篇主要通过几个小例子把angular的常用组件简单介绍一下
2. data binding
下面是一个简单通过quantity和costs计算Total的示例
@H_301_12@<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}}@H_301_12@ </div> </div>
输出结果
下面描述一下这段代码是如何运行的,这段代码看起来像一个带着一些新的标记的普通html文件,在angular中,这样的文件叫template,当angular运行时,它会从这个template中去解析处理这些新的标记,并且加载、转换和呈现以dom的形式。
第一种标记在angular中被称为directives,他们为一些属性或者元素增加特殊的功能,比如上面的ng-app属性,angular识别到它会自动初始化为angular应用程序。angular也为input元素增加一些扩展的功能,比如接下来的ng-model,他可以将input的值存储到一个变量中,也可以把变量的值赋予到input中,这这绑定是live的绑定,一个变化另一个随之变化,称之为双向绑定。
第二种标记是带双花括号的 {{ expression | filter }},当angular的编译器遇到这种标记时会用表达式最终的值去代替这个标记。在template中的表达式是一种类javascript的变量,angular可以对这些表达式进行读写。应该注意的是这些变量都不是全局变量,仅仅就像是javascript函数中的变量一样。angular给这些变量scope的概念,以便让他们以model的形式在文档的其他部分可以访问到。对于这个例子来说,angular对这些directives的理解就是:把两个输入框的值取出来然后相乘。
上面的例子还包含了一个filter,filter的作用主要是把表达式的值展现给用户,在这个例子中currency filter将数值最终转换为货币格式输出
3.页面逻辑:Controllers
让我们增加一些逻辑,可以用不同的货币输入,计算和支付这个货单。
index.html
@H_301_12@<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}}@H_301_12@</option> </select> </div> <div> <b>Total:</b> <span ng-repeat="c in invoice.currencies"> {{invoice.total(c) | currency:c}}@H_301_12@ </span> <button class="btn" ng-click="invoice.pay()">Pay</button> </div> </div>
invoice1.js
angular.module('invoice1',[])
.controller('InvoiceController',@H_853_301@function() {
@H_853_301@this.qty = 1;
@H_853_301@this.cost = 2;
@H_853_301@this.inCurr = 'EUR';
@H_853_301@this.currencies = ['USD','EUR','CNY'];
@H_853_301@this.usdToForeignRates = {
USD: 1,EUR: 0.74,CNY: 6.09
};
@H_853_301@this.total = @H_853_301@function total(outCurr) {
@H_853_301@return @H_853_301@this.convertCurrency(@H_853_301@this.qty * @H_853_301@this.cost,@H_853_301@this.inCurr,outCurr);
};
@H_853_301@this.convertCurrency = @H_853_301@function convertCurrency(amount,inCurr,outCurr) {
@H_853_301@return amount * @H_853_301@this.usdToForeignRates[outCurr] / @H_853_301@this.usdToForeignRates[inCurr];
};
@H_853_301@this.pay = @H_853_301@function pay() {
window.alert("Thanks!");
};
});
输出
在javascript文件中包含了一个controller,确切的说这个文件包含了一个构造器函数,创建了一个真实的controller实例。controller的作用就是将变量和函数暴露给表达式和directives。
除了一个包含controller的javascript文件外,还在html中增加了一个ng-controller directive。这个directive(InvoiceController)告诉angular它负责带该directive的元素和元素下的所有子元素。InvoiceController as invoice这个语法是告诉angular实例化InvoiceController controller并且把它保存在invoice变量在当前scope中。
我们还修改了页面所有的表达式中的变量给他们加上了invoice的前缀(controller实例)。可能会选择的货币在controller中被定义和初始化,并通过ng-repeat的方式添加到了html中。像在controller中定义total函数的那样,我们也能把{{ invoice.total(…) }}绑定到html中。
我们还给pay按钮通过ng-click directive增加了点击事件,一旦点击就会弹出一个相应的表达式。
4.业务逻辑:Services
现在,InvoiceController包含了这个示例的所有逻辑。当程序需要扩展时,有个比较好的方法是把业务逻辑从页面逻辑controller中抽取出来形成一个service,而且service也可以被其他模块复用,分层思想在angular中的一个体现。需要更改数据的话只需要在service中更改即可。
finance2.js
angular.module('finance',[])
.factory('currencyConverter',@H_853_301@function() {
@H_853_301@var currencies = ['USD','CNY'];
@H_853_301@var usdToForeignRates = {
USD: 1,CNY: 6.09
};
@H_853_301@var convert = @H_853_301@function (amount,outCurr) {
@H_853_301@return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
};
@H_853_301@return {
currencies: currencies,convert: convert
};
});
invoice2.js
angular.module('invoice',['finance'])
.controller('InvoiceController',['currencyConverter',@H_853_301@function(currencyConverter) {
@H_853_301@this.qty = 1;
@H_853_301@this.cost = 2;
@H_853_301@this.inCurr = 'EUR';
@H_853_301@this.currencies = currencyConverter.currencies;
@H_853_301@this.total = @H_853_301@function total(outCurr) {
@H_853_301@return currencyConverter.convert(@H_853_301@this.qty * @H_853_301@this.cost,outCurr);
};
@H_853_301@this.pay = @H_853_301@function pay() {
window.alert("Thanks!");
};
}]);
首先,把convertCurrency函数和currencies数组移到finance2.js文件中,但是controller中怎么才可以访问到finance2中的函数呢?
这时Dependency Injection就派上用场了,Dependency Injection (DI)是一种软件设计模式,主要处理对象和函数的创建并且怎么样获得这些依赖,angular的一切(directives,filters,controllers,services,…)都可以被创建并且通过Dependency Injection连接。在angular中这样的DI容器叫做injector。
为了使用DI,那么就需要一个地方,所有的东西在这里被注册。在angular中,这就是modules干的事。当angular启动时,它会使用在ng-app directive定义的名字的module的配置,并且使用该module需要依赖的所有module的配置。
在上面的示例中,template包含了一个directive ng-app=”invoice”,它告诉angular用invoice作为这个应用程序的main module,代码片段angular.module(‘invoice’,[‘finance’])指明invoice module依赖于finace module,angular使用InvoiceController以及currencyConverter service。
InvoiceController怎么样才能拿到currencyConverter函数的引用呢?在angular中其实非常简单,就是在构造函数的参数中定义。injector能按照争取的顺序创建对象,并且可以把之前创建的对象传递到依赖他们的对象的构造函数中。在我妈的例子中,InvoiceController的构造函数有一个叫currencyConverter的参数。这样angularj就知道controller和service之间的依赖关系。
最后要说的是service传递函数以一个数组的方式给controller,这个数组的第一个是controller和service依赖之间约定好的一个name,第二个是service中实际使用的name,如果学过spring 应该很容易理解,这样在代码进行改动时比较容易,只需改动被依赖的代码即可,而且在javascript中也很常用,因为javascript经常被压缩,变成a. b. c. 这样的代码。