AngularJS概念概述和第一个使用例子
点击查看AngularJS系列目录
转载请注明出处:http://www.cnblogs.com/leosx/
概念概述
本节使用一个简单的例子简要介绍了AngularJS的重要组成部分。
概念 | 描述 |
模板(Template) | HTML的附加标记 |
指令(Directives) | 通过元素或者客户属性去扩展HTML |
模型(Model) | 用户和界面交互的数据的模型。 |
上下文(Scope) | 语境上下文,这样控制器,指令和表达式可以访问它里面的数据。 |
表达式(Expressions) | 可以从Scope(上下文)中访问变量和函数。 |
编译器(Compiler) | 解析模板和实例化指令和表达式 |
过滤器(Filter) | 格式表达式的值以显示给用户 |
视图(View) | 用户所看到的(DOM) |
数据绑定(Data Binding) | 模型和视图间同步数据的动作 |
控制器(Controller) | 试图(view)背后的业务逻辑 |
依赖注入(Dependency Injection) | 创建对象和函数的工厂 |
注入器(Injector) | 依赖注入的容器 |
模块(Module) | 对应用程序的不同部分,包括控制器,服务器,过滤器,指令进行配置注射器的容器 |
服务(Service) | 对于试图可重用的业务逻辑独立的功能部分 |
第一个例子:数据绑定
在下面的例子中,我们将建立一个表格来计算总价格。
<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和一些新的标记的组合。在AngularJS中,这样的文件称为模板。当AngularJS启动应用程序时,它会使用编译器去分析和处理来自新的标记标识的模板。然后在加载,改造,呈现一个新的DOM到用户视图。
第一个新的标记是指令(directives)。它是HTML元素或者属性的一个特殊的行为。在上面的例子当中,我们使用了 ng-app 属性,这是一个指令,用于自动初始化我们的应用程序。AngularJS还为input元素定义了一些额外的指令,用于增加一些行为来扩展元素。例如:ng-model 指令,用来存储或者更新输入字段的值。
自定义指令访问DOM:在一个AngularJS应用程序中,唯一的一个允许去访问DOM的地方就是指令的范围内(Scope)。这点很重要,因为访问DOM元素是一件很难预测,也很难测试的事情。如果您需要直接访问DOM,你应该写一个自定义的指令去做这件事。
在指令章节,我们会去介绍如何自定义。
第二种新的标记是双花括号 {{表达|过滤器}}
,当编译器遇到这个标记,将其与标记的评估值替换。在模板中的表达式是一个类似于JavaScript的代码段,允许读取和写入变量。请注意,这些变量不是全局变量。就像AngularJS函数和这些变量在一个function函数里面一样,Angular可以去访问。在上面的例子当中,我们的花括号表达式的功能是计算数量和单价,以获得总价格。后面的过滤器使得我们的输出格式为金钱格式(这里加了一个$符号表示是美元为单位的)。在这个例子中,我们可以看到,AngularJS提供了一个绑定:当输入值发生变化,表达式的值会自动重新计算并且去更新DOM与之对应的值。这使用到的概念是双向数据绑定(学过WPF或者Silverlight的同学一定很清楚明白的)。
添加UI逻辑:控制器(controller)
让我们添加更多的逻辑,使我们能够进行不同货币的总价格计算。
第一个文件:invoice1.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控制器的实例。控制器的目的就是为我们的表达式和指令去提供一些变量和方法。
除了增加了新的文件之外,我们还在HTML代码中增加了一个ng-controller
指令,这个指令就是在告诉AngularJS,去创建一个InvoiceController
去负责这个元素,以及这个元素下的所有子元素。语法:ng-controller="InvoiceController as invoice"
是告诉AngularJS去创建一个控制器,并重命名此控制器为invoce,这样下面访问控制器中的变量的时候,就可以使用invoce这个昵称去访问控制器所在的Scope中的变量了,HTML 中,还使用了ng-repeat
指令去循环指定的元素。也定义了一个方法total (function)方法去计算总价格。
同样,这种结合是很灵活的,即当数据变化时,DOM会自动更新显示变化后的结果。这就省却了我们去修改DOM元素。我们使用了ng-click
指令去绑定到了按钮点击事件上。这样,当点击按钮的时候,就会触发与controller
的scope
所对应的方法进行执行。
在新的JavaScript文件,我们还在Module中注册了我们的控制器模块。我们将在下一节讨论模块(module),下图显示了所有的东西是如何在Controller里面对应显示出来的。
独立的业务逻辑:服务
眼下,InvoiceController包含我们的例子中的所有逻辑。当我们的逻辑变得复杂之后,我们应该把一些可以独立出来的业务逻辑提出来,放到服务里面去,这样这些独立的业务逻辑块儿就可以重复使用了。之后,我们就可以从网络上加载一些独立的业务逻辑,如装载并调用雅虎的财经API来实现汇率的转换,而我们的控制器不用变。
服务文件: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!");
};
}]);
HTML文件: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>
效果图:
这次又有什么变化呢?
我们新建了一个文件:finance2.js 并且把 convertCurrency
功能移动到了文件当中去。但是我们又如何使得控制器依旧保持着这个功能呢?
这就是依赖注入的用武之地。依赖注入(DI)是软件设计的一部分,它实现了如何创建对象(object)和功能函数(function),以及如何正确找到它们的依赖关系。在Angular中,所有的东西(指令,过滤器,控制器,服务,...)的创建都有使用依赖注入。在Angular中,DI的容器是注入器(injector)
要使用DI,我们首先是要在需要使用的地方进行注册。在Angular中,我们是在模块(modules)中进行注册的。当Angular启动时,它会执行模块的配置(config)然后根据ng-app
指令开始执行,在执行前,模块会根据配置的名称去加载所有依赖的模块。
在上面的例子当中,包含了一个指令ng-app="invoice2"
。这是在告诉Angular,应用程序的主模块适用名为:invoice2
的模块作为主模块。代码段:angular.module('invoice2', ['finance2'])
指定了invoice2
的模块依赖于finance2
模块。也就是说,InvoiceController
控制器使用了currencyConverter
服务中的功能。执行到这里Angular知道该应用程序的所有部分了,接下来就是去创建它们了。在上一节我们的控制器使用工厂函数来创建的。在服务方面有多种方式来定义自己的工厂(见服务指南)。在上面的例子中,我们在服务的工厂(factory)中使用了一个函数去返回了一个currencyConverter
函数功能。
回到最初的问题:如何在InvoiceController控制器中得到一个服务中的currencyConverter功能?在Angular中,这是通过在构造函数中简单地定义参数进行实现的。有了这个,injector能够根据顺序去创建正确的对象,并且在创建前,会先调用创建依赖对象并调用它们的工厂函数。在InvoiceController
中,有一个currencyConverter
的参数,有了它,Angular就知道控制器和服务之间的依赖性,并调用与服务实例作为控制器的参数。
无论是上一个例子还是这一个例子,我们都可以看到,我们在module.controller
函数中,有一个数组,数组首先包含了控制器需要服务相关的名称,它就是在注册所依赖的模块。数组中的最后一项是控制器构造函数。Angular使用此数组语法定义的依赖关系,使得减少了代码污染DI。
访问后台服务
让我们来实现一下我们上面说到的,使用雅虎汇率服务API,去通过网络获得实时的汇率信息。
第一个文件: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
服务,使用了$http
,$http
是一个AngularJS内置的用于访问服务器的后端数据的服务。$http
使用的是XMLHttpRequest
技术和 JSONP 进行传输.
AngularJS概念概述和第一个使用例子的更多相关文章
- 不用搭环境的10分钟AngularJS指令简易入门01(含例子)
不用搭环境的10分钟AngularJS指令简易入门01(含例子) `#不用搭环境系列AngularJS教程01,前端新手也可以轻松入坑~阅读本文大概需要10分钟~` AngularJS的指令是一大特色 ...
- 按照鬼哥学so变化,四,第一章的例子
跟随鬼哥伦比亚科学so变化,四.第一章的例子 图纸/文化 听鬼哥说故事 ---------------------------------------------切割线--------------- ...
- 从源码编译安装PCL并运行第一个小例子
如何通过源码编译方式安装PCL 对于很多想学习PCL的同学而言,往往会被如何安装困扰很长时间.我就是这其中的一员,为了不让大家在安装问题上浪费太多时间,我决心写下这篇小小的随笔,希望对大家有所帮助. ...
- Angular开发者指南(二)概念概述
template(模板):带有附加标记的模板HTML directives(指令):使用自定义属性和元素扩展HTML model(模型):用户在视图中显示的数据,并与用户进行交互 scope(作用域) ...
- 针对angularjs下拉菜单第一个为空白问题处理
angularjs 的select的option是通过循环造成的,循环的方式可能有 ng-option 或 者 <option ng-repeat></option ...
- 《hadoop权威指南》关于hive的第一个小例子的演示
本文是<hadoop权威指南>关于hive的小例子,通过这个例子可以很好地看出来hive是个什么东西. 前提是已经配置好hive的远程连接版本的环境,我是用了MYSQL数据库保存元数据. ...
- springmvc入门的第一个小例子
今天我们探讨一下springmvc,由于是初学,所以简单的了解一下 springmvc的流程,后续会持续更新... 由一个小例子来简单的了解一下 springmvc springmvc是spring框 ...
- NodeJS系列~第一个小例子,实现了request.querystring功能
返回目录 百度百科上: Node.js是一套用来编写高性能网络服务器的JavaScript工具包,一系列的变化由此开始,在Node中,Http是首要的.Node为创建http服务器作了优化,所以在网上 ...
- Camel In Action 阅读笔记 第一部分概述 + 第一章概述 认识Camel
第一部分: 最开始的一小步 Apache Camel 是一个开源集成框架,其目的是让系统集成变得更加简便,在本书的第一章中,我们会为您介绍它并向您展示它是如何在大型企业应用中做好集成工作.您也会了解到 ...
随机推荐
- 总结切面编程AOP的注解式开发和XML式开发
有段日子没有总结东西了,因为最近确实有点忙,一直在忙于hadoop集群的搭建,磕磕碰碰现在勉强算是能呼吸了,因为这都是在自己的PC上,资源确实有点紧张(搭建过程后期奉上),今天难得大家都有空(哈哈哈~ ...
- MVC配置伪静态
提出需求 伪静态能提高搜索引擎收录,还不影响硬盘寿命,是一个不错的选择,但是会增加CPU和内存开销,由于时候也需要实现伪静态. web.config配置 <system.webServer> ...
- 去掉vue地址栏中分隔#问题
你需要开启HTML5 History 模式vue-router 默认 hash 模式 -- 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载.如果不想要很 ...
- 博客已转移至 http://blog.yemou.net/
博客已转移至 http://blog.yemou.net/ , 该网站是一家更加专业的网站,欢迎大家过去交流
- Spring中实现文件上传
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt110 实现图片上传 用户必须能够上传图片,因此需要文件上传的功能.比较常见 ...
- 汇编指令-位置无关码(BL)与绝对位置码(LDR)(2)
位置无关码,即该段代码无论放在内存的哪个地址,都能正确运行.究其原因,是因为代码里没有使用绝对地址,都是相对地址. 位置相关码,即它的地址与代码处于的位置相关,是绝对地址 BL :带链接分支跳转指令 ...
- 原创:LNMP架构部署个人博客网站 禁止转载复制
nginx编译安装步骤 ①. 检查软件安装的系统环境 cat /etc/redhat-release uname -r ②. 安装nginx的依赖包(pcre-devel openssl-devel) ...
- ps图层面板上的【透明度】与【填充】的区别
为文字添加投影,分别调图层面板上的[透明度]与[填充]的值你就知道区别了. 如上图降低填充的数值,结果只对文字颜色有影响却对投影毫无影响. 而如上图,调整不透明度的时候对文字颜色与投影均产生效果. 这 ...
- win8下安装VC6出现兼容性问题的解决办法
重装系统之后(win8的系统),发现VC6安装出现兼容性问题,花了一些时间解决,有出现的问题都差不多在下面链接的总结中,写的很详细: http://www.docin.com/p-1126120829 ...
- 控制结构(10) 指令序列(opcode)
// 上一篇:管道(pipeline) 发现问题 在一个正式项目的开发周期中,除了源代码版本控制外,还存在着项目的配置/编译/打包/发布等各种高频但非"核心"的脚本代码.职业程序员 ...