博弈AngularJS讲义(2) - 核心概念
基本概念
在使用AngularJS之前,我们有必要梳理一下AngularJS核心概念及实现原理,以便于写出结构良好的代码。本章我们将结合简单的实例简要介绍Angular的重点概念,我们将附上AngularJS官方文档上的原文,帮助大家更好的理解概念:
- Template
官方解释 “HTML with additional markup” ,模板就是HTML和CSS编写的片段,用来展现应用的视图。您可以通过实现AngularJS编译器识别的指令(directive), 扩展HTML的标签或属性。 - Directives
官方解释"extend HTML with custom attributes and elements", 指令即通过AngularJS定制化的HTML标签或属性 - Model
官方解释"the data shown to the user in the view and with which the user interacts", 熟悉MVC框架的人都知道Model就是数据,AngularJS通过作用域(scope)保持数据模型与视图的双向同步。如果一方改变,另一方会自动随之更新状态。 模型中的数据一般为Javascript的对象,数组或基本类型。 - Scope
官方解释"context where the model is stored so that controllers, directives and expressions can access it", 及Model的作用域或上下文,控制器(Controllers)、指令(Directives)、表达式(Expression)只能访问相应作用域内的Model, 除非是通过Angular底层API访问其他作用域。作用域是分层组织的,层级关系与DOM结构是绑定(除了特定的指令会自建Scope) - Expressions
官方解释"access variables and functions from the scope", 通过AngularJS表达式可以访问作用域内的变量或函数。 - Compiler
官方解释"parses the template and instantiates directives and expressions",编译器,AngularJS在页面加载完DOM之后会对HTML预处理,编译器负责解析模板(template)和初始化指令和表达式。 - Filter
官方解释"formats the value of an expression for display to the user", 过滤器, AngularJS提供了内建的过滤器或自定义的过滤器对表达式的内容进行格式化输出。 - View
官方解释"what the user sees (the DOM)",即视图。 - Data Binding
官方解释"sync data between the model and the view", AngularJS通过双向数据绑定,同步模型与视图。 - Controller
官方解释"the business logic behind views",控制器封装了业务逻辑。 - Dependency Injection
官方解释"Creates and wires objects and functions",依赖注入,类似于Spring, AngularJS会将对象缓存与注入器内部($injector), 通过名字查找对象实例,如果没有找到会根据相关的工厂创建实例,自动加载依赖的组件,让代码更简洁清晰。 - Injector
官方解释"dependency injection container", 即注入器,类似于Spring容器,AngularJS为每个应用(ng-app)提供唯一的注入器,用于缓存对象实例。 - Module
官方解释"a container for the different parts of an app including controllers, services, filters, directives which configures the Injector", 模块,AngularJS可以让我们写出高内聚性的app,通过相关的控制器,服务,指令组织在模块中,每个模块都有相应的唯一的注入器,管理配置模块的内部组件。
实例讲解
接下来我们通过具体的实例讲解上面涉及的概念。
实例1: 数据绑定
<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>
页面预览:
让我们具体看上面的例子:
- AngularJS把上面的一段HTML看作一个"Template", 在HTML加载完成后,Angular从ng-app处启动应用,由Angular编译器"Compiler"解析新的标签或者属性,初始化Angular应用的上下文,绑定模型和视图。通过此过程加载、转换并展现出来的DOM就是所谓的视图。
- 新的标记被称为指令"Directive",这种标记通过AngularJS被赋予了某些特殊行为。例如 "ng-app"触发Angular自动初始化相应的应用模块, 如下图所示:
- Angular内建了指令给<input>添加了额外的行为。
- ng-model指令绑定了作用域内同名的属性,在这个例子中input中的text发生改变,绑定的model(qty | cost)值也随之更新, 反之亦然。
- {{ expression | filter }} AngularJS会这类标记识别成表达式和过滤器,表达式类似于JS片段,允许我们 读写作用域内的模型(model),如例子所示,我们通过表示可以看到input的中绑定的变量的乘积。
- 上述表达式还有个filter,用于前面的表达式结果进行格式化输出, 用法类似于Linux里面的管道"|".
该例子展示了AngularJS的双向数据绑定. 当input值改变时,会重新计算表达式并在DOM中实时更新。
实例二: 基于实例1 通过Controller加入逻辑
实现Controller (invoice.js)
angular.module('invoice', []) .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] / this.usdToForeignRates[inCurr]; }; this.pay = function pay() { window.alert("Thanks!"); }; });
HTML
<div ng-app="invoice" 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> <button class="btn" ng-click="invoice.pay()">Pay</button> </div> </div>
实例讲解:
- 在这个例子中我们我们实现了名为"invoice1"的应用模块名在页面中通过ng-app指令引入. 在"invoice1"模块中定义了"InvoiceController"控制器,并通过ng-controller在页面中绑定, 注意ng-controller必须出现在和"invoice1"绑定的元素或者子元素中. 控制器暴露了该模块作用域内可以使用的变量或者函数(this.*)给相应的表达式和指令.
- Angular提供controller()构造器给我们实现自己的Controller.
- 在ng-controller我们用“InvoiceController as invoice” 给controller去了个别名,通知AngularJS在注入器给该控制器的实例取名为invoide,在HTML子元素中可以通过别名引用控制器里面的变量或者函数。
- 我们同样注意到ng-repeat指令,可定绑定数组或者对象类型,迭代DOM操作
- ng-click是另外一个AngularJS指令,在相应DOM上绑定click时间及与之对应的行为。
工作原理如下图所示:
实例三 : 基于实例二将于View无关的业务逻辑抽取到service组件里面
但应用变得愈加复杂的时候有必要把与视图无关的业务逻辑抽取成service组件,有经验的程序员可以联想JAVA EE或者.NET中的MVC架构。这些service是可以重用的。让我们重构一下上面的代码:
1. 将finance领域相关的逻辑抽取成finance.js
angular.module('finance', []) .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 }; });
2. 简化invoice.js的代码如下:
angular.module('invoice', ['finance']) .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!"); }; }]);
我们分析一下重构的代码:
1. 我们把convertCurrency函数挪到了finance.js中,利用依赖注入机制在controller的构造函数中声明注入finance service对象。Angular组件都可以通过依赖注入的方式(injector)连接起来。
2. 提到依赖注入,我们肯定想到一定会有个地方(容器)注册、存放、管理依赖,AngularJS通过模块(module)把相关的组件放到一起。在Angular启动应用时,会通过ng-app的配置项,已经定义在module代码里面的配置项初始化容器。
3. 在上面的例子中ng-app="invoice"这个指令告诉Angular用invoice模块作为应用的主模块。代码angular.module('invoice', ['finance'])声明invoice模块依赖finance模块,即InvoiceController中用到了finance模块中的currencyConverter服务controller('InvoiceController', ['currencyConverter',function....]。
4. AngularJS提供了多种方式声明依赖,本例通过构造函数的参数列表中定义依赖,最后一个参数为构造函数。
5. 本例中通过AngularJS的factory方法定义service, 例如factory('currencyConverter', function(){...}, 在后续的例子中我们将会详解service.
本例具体原理如下图所示:
以上示例来自于官方文档,在后续的章节中我们将结合更多具体的代码示例详解每个具体的概念。
相关推荐
问题描述在编写导入指令的时候,需要将函数绑定到指令中,并传入一个参数。<button ng-hide="importing" class="btn btn-warning btn-sm" type="