这部分通过示例简略的介绍了 Angular 的所有重要内容, 详情见向导部分.
概念 | 描述 |
---|---|
Template(模板) | 带有扩展标记的 HMTL 片断 |
Directives(指令) | 通过自定义属性和元素扩展 html |
Model(模型) | 展示给用户或者用户操作的数据 |
Scope(作用域) | 存储模型的上下文环境, 在这个上下文环境中控制器, 指令和表达式才能访问到它 |
Expressions(表达式) | 访问作用域中变量和函数 |
Compiler(编译器) | 解析模板, 实例化指令和表达式 |
Fiter(过滤器) | 格式化展示给用的表达式的值 |
View(视图) | 用户看到的内容 (DOM) |
Data Binding(数据绑定) | 模型和视图之间的数据同步 |
Controller(控制器) | 视图之后的业务逻辑 |
Dependency Injection(依赖注入) | 创建和自动装载对象和函数 |
Injector(注入器) | 用于实现依赖注入的容器 |
Module(模块) | 包括控制器, 服务, 过滤器, 指令在内的 web 应用的不同部分, 可以配置注入器的容器 |
Service(服务) | 可重用的, 独立于视图的业务逻辑 |
在下面的例子中, 我们将创建一个表单来计算订单的金额.
首先输入数量和单价, 它们的乘积就是订单的总金额:
index.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, 只是多了些新的标记. 在 Angular 中, 像这样一个文件被叫做 "template(模板)". Angular 启动应用时, 它会使用 "compiler(编译器)" 解析并且处理模板中的新标记. 这些被加载, 转换和渲染的 DOM 就是 "view(视图)".
第一种新的标记叫做 "directives(指令)". 指令适用于在 HTML 属性或元素中添加特定的行为. 上面示例中, 我们使用了指令
属性, 它的作用就是初始化我们的应用. Angular 也为
- ng-app
元素定义了指令, 通过这个指令可以为元素添加额外的行为. 通过
- input
指令可以将输入框中的值存储到变量中, 或将变量中的值更新到输入框中.
- ng-model
第二种新的标记是双花括号
: 当编译器遇到这种标记时, 它会用实际的值去替换标记本身. 模板中的 "expression(表达式)" 就像是 Javascript 代码片段, 它可以对变量进行读写操作. 需要注意的是, 那些变量并不是全局变量. 就在定义在 Javascript 函数中的变量属于某一个作用域一样, Angular 也为表达式中访问的变量提供了一个 "scope(作用域)". 这些存储在作用域变量中的值被称为 "model(模型)". 在上面的示例中, 这些告诉 Angular: "从输入框中取得数据, 并将它们相乘".
- {{ expression | filter }}
上面的示例中包含一个 "filter(过滤器)". 过滤器可以将表达式的值格式化后展示给用户. 在上面的示例中, 过滤器
将一个数字格式化为货币的格式进行输出.
- currency
重要的一点是, 在上面的示例中, Angular 提供的动态绑定机制: 输入的值无论什么时候改变, 表达式的值都会自动重新计算, DOM 元素也会自动更新显示的值. 这就是 Angular 所提供的模型与视图之间的 "双向数据绑定机制".
现在让我们在上面的示例中添加一些业务逻辑, 使我们可以使用不同的货币输入, 计算金额并完成支付.
invoice.js
- 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] / 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>
- <button class="btn" ng-click="invoice.pay()">
- Pay
- </button>
- </div>
- </div>
做了哪些修改
首先, 添加了一个 JavaScript 文件, 其中有个被叫做 "controller(控制器)" 的函数. 更确切的说, 这个文件中包含一个构造函数, 这个构造函数可以创建控制器实例. 控制器的作用是给表达式和指令暴露变量和函数, 供它们使用.
除这个包含控制器代码的 JavaScript 文件以外, 我们还在 HTML 中添加了
指令. 这个指令告诉 Angular, 这个新的控制器
- ng-controller
会负责管理包含它的元素及其所有子元素.
- InvoiceController
这种语法告诉 Angular 要初始化这个控制器, 并且将它赋值给当前作用域的变量
- InvoiceController as invoice
.
- invoice
我们还修改了页面中读写变更的所有表达式, 给他们添加了控制器的实例名称
作为前缀. 我们在控制器中定义一些货币的各类, 并且通过指令
- invoice.
把它们添加到模板中. 由于控制器还还有个
- ng-repeat
函数, 我们还能将它的结果值通过表达式
- total
绑定在 DOM 元素上.
- {{ invoice.total(...) }}
当然, 这个绑定也是动态的, 换句话说, 无论函数结果什么时候发生变化, DOM 都会自动修改其展示的值. 付款按钮使用了指令
, 无论什么时候点击这个按钮, 它将计算相应表达式的值.
- ngClick
在这个新增的 JavaScript 文件中, 我们还创建一个 module(模块), 并且将控制器注册在了这个模块上. 我们将在下一部分对模块进行讨论.
下图展示的是加入了控制器之后, 应用中的每一部分是如何协作的:
现在, 控制器
包含了示例中的所有逻辑. 当这个应用继续扩展, 那么最好的作法是将与视图无关的业务逻辑从控制器中移动至 "service(服务)" 中, 这样它都能更好的被应用的其他部分重用. 日后, 我们也可以修改脚本, 将它改成从网络中加载汇率, 例如 Yahoo 的金融 API, 而不会修改控制器.
- InvoiceController
我们重构下我们的示例, 将货币转换移动到另一个文件的 service(服务) 中.
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(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>
- <button class="btn" ng-click="invoice.pay()">
- Pay
- </button>
- </div>
- </div>
有哪些变化? 我们将函数
和已经定义好的货币变量移动到
- convertCUrrency
中. 但是控制器是怎样引用已经分离出去的函数的呢
- finance2.js
轮到 "Dependency Injection(依赖注入)" 出场了. Dependency Injection (DI)依赖注入是一种软件设计模式, 它解决了对象和函数是如何得到已经创建好的并且被它们所依赖的对象的引用的. Angular 中的每个部分 (指令, 过滤器, 控制器, 服务...) 都是通过依赖注入机制来创建和关联的. 在 Angular 中, DI 窗口被称叫 "injector".
为了使用 DI(依赖注入), 所有需要协作的部件都需要统一注册的一个位置. 在 Angular 中, "mudules(模块)" 就是为解决这个问题. Angular 从指令
开始启动, 它会根据模块名称加载模块配置, 包括此模块所依赖的所有模块.
- ng-app
在上面的示例中: 模板中包含了指令
. Angular 就会使用模块
- ng-app="invoice2"
作为整个应用的主模块. 代码段
- invoice2
指定了模块
- angular.module('invoice2', ['finance2'])
依赖于模块
- invoice2
. 这样, Angular 就即可以使用控制器
- finance2
以及服务
- InvoiceController
.
- currencyConverter
既然 Angular 了解应用所有部分的定义, 那么就可以来创建它们. 在上一段中, 我们是使用一个工厂方法创建了控制器. 而对于服务来说, 有多种定义它们工厂的方式 (见服务指南). 在上面的示例中, 我使用一个返回
函数的函数作为创建
- currencyConverter
服务的工厂.
- currencyConverter
回到最初的问题: 控制器
是怎样获得函数
- InvoiceController
的引用的? 在 Angular 中, 通过定义构造函数的参数就可以做到这一点. 通过这种方式, 注入器 (injector) 根据正确的依赖顺序创建这些对象, 并将创建好的对象传入依赖它们的工厂中. 在我们的示例中, 控制器
- currencyConverter
的构造函数有一个命名为
- InvoiceController
的参数. 通过这个参数, Angular 便知道控制器与服务之间的依赖关系, 并且把服务对象作为参数来调用控制器的构造函数.
- currencyConverter
这次改动中的最后一点是, 我们将一个数组传入了
函数中, 而不再是一个普通函数. 首先, 这个数组包含了控制器所依赖的服务的名称. 数组的最后一个元素则是控制器的构造函数. Angular 通过这种数组的语法定义依赖关系, 使得依赖注入发生在压缩代码之后, 因为代码压缩通常都会将控制器构造函数的名称重命名为很短的名称, 比如
- module.controller
.
- a
让我们通从 Yahoo 金融 API 获取汇率来完成我们的示例. 下面的示例将展示 Angular 是怎样做的:
invoice3.js
- angular.module('invoice3', ['finance3'])
- .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!");
- };
- }]);
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&callback=JSON_CALLBACK';
- 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.jsonp(url).success(function(data) {
- var newUsdToForeignRates = {};
- angular.forEach(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,
- refresh: refresh
- };
- }]);
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>
- <button class="btn" ng-click="invoice.pay()">
- Pay
- </button>
- </div>
- </div>
修改了什么?
模块的
- finance
服务使用了
- currencyConverter
服务, 一个 Angular 内置的服务, 使用它可以访问后台服务器.
- $http
就是封装了
- $http
和
- XMLHttpRequest
传输.
- JSONP
来源: http://www.cnblogs.com/lzj0616/p/6510056.html