@iamfox
2015-10-19T12:07:29.000000Z
字数 15096
阅读 2070
angular.js 前端 汇银
AngularJS是一个js框架,用来开发浏览器客户端显示的动态网页。
它通过一些自定义的JS和HTML标签语法扩展了静态HTML的功能,这样就能通过HTML标签或属性来处理动态信息。
AngularJS主要有以下五个特性:
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><!-- 这里引入了AngularJS,就像用JQuery一样--><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><!--这里给name对象赋了个值--><div ng-app="" ng-init="name='World'"><!--这里把name对象值又输了出来Hello World-->Hello {{ name }}!</div></body></html>
这段代码另存为html文件以后,在浏览器打开就能看到效果。

为了方便运行,推荐去http://runjs.cn/code,只要往窗口中粘贴一下上面的代码,就可以直接预览效果了。
指令是一个自定义的HTML标签,比如<xxx></xxx>。或者自定义属性,比如<div xxx></div>。
Angular自带的常见的属性指令有:ng-app、ng-init、ng-model、ng-bind、ng-repeat等等。前面例子就用到了ng-app、ng-init两个自定义属性指令
每个指令背后都有一些JS代码来实现其功能,当浏览器在解析HTML时,AngularJS也在解析DOM上的这些属性指令,然后执行JS。
在HelloWorld例子里的ng-init,它的作用是在被解析到的时候,将'World'这个字符串,赋给了name变量,ng-init=""双引号内直接写了JS代码。由于AngularJS的双向绑定实时更新特性,下面那个{{ name }}占位符所在位置立马就变成了name的值。
ng-app是一个根元素一样的指令,用来标记AngularJS的作用域。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><!-- 如果把`ng-app`加在上面这个`div`标签中,那AngularJS只会去解析这个`div`里的AngularJS元素。 --><div ng-app=""></div></body></html>
<!DOCTYPE HTML><!-- 如果这样加,那整个页面的AngularJS元素都会被解析。--><html ng-app=""><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div></div></body></html>
ng-init是用来初始化数据的。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body ng-app=""><!-- 初始化了两个数字 --><div ng-init="quantity=1;price=5"></div><!-- 初始化了一个数组 --><div ng-app="" ng-init="names=['Tom','Jerry','Gaffey']"></div></body></html>
还可以初始化对象。
如HelloWorld例子中所示,数据绑定标签是{{ expression }}。
{{}}中可以直接写变量名来输出值,也可以写JS表达式。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body ng-app=""><div ng-init="quantity=12;price=5"><!-- 这里会输出60 --><p>总价: {{ quantity * price }}</p></div><div ng-init="names=['Tom','Jerry','Gaffey']"><!-- 这里会输出Tom --><p>名字为: {{ names[0] }}</p></div></body></html>

<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app=""><!-- 输入框和一个js中的model:name变量绑在了一起 --><p>请输入任意值:<input type="text" ng-model="name"></p><p>你输入的为: {{ name }}</p></div></body></html>
你往输入框填值,就是在实时修改js中的name变量,也同时就影响了表达式的输出。就这样简单的标签就实现了页面的实时更新,不再需要那些臃肿的js DOM操作代码。
这只是个单向绑定例子,在后面学习控制器时会看到ng-model的双向绑定效果。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app=""><p>请输入一个名字:<input type="text" ng-model="name"></p><p>Hello <span ng-bind="name"></span></p></div></body></html>
ng-bind其实就是把{{}}表达式换成了标签,这样就能避免在数据加载完成之前,比如ajax还在取数据时,让用户看到了不该看到的{{}}。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><!-- 初始化了一个status变量为false--><div ng-app="" ng-init="status=false"><!-- 每点一下button,就执行了一次'status = !status',于是status变量就在true和false之间来回切换--><button ng-click="status= !status">隐藏/显示</button><!-- div里的内容,通过一个'ng-hide'标签控制是否显示,也一直随着status在显示和隐藏中切换。--><div ng-hide="status"><p>请输入一个名字:<input type="text" ng-model="name"></p><p>Hello <span ng-bind="name"></span> </p></div></div></body></html>

MVVM是Model-View-ViewMode的简称,Model包含了业务实体和逻辑,来自数据库。View是我们最后要呈现的页面效果,ViewModel是从Model中提取的,用于维护特定View的数据和方法。
Model和ViewModel通过$scope对象互动,可以监听到Model的变化,然后通过View来渲染,View通过$routeProvider对象支配。
使用MVVM模式有几大好处:
低耦合:View可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可重用性:可以把一些视图的逻辑放在ViewModel里面,让很多View重用这段视图逻辑。
独立开发:开发人员可以专注与业务逻辑和数据的开发(ViewModel)。设计人员可以专注于界面(View)的设计。
可测试性:可以针对ViewModel来对界面(View)进行测试。
你的需求不会总是简单到直接把已有变量打出来就行了,你需要写一些js脚本来获取或计算你想要的值,再输出,这件事由控制器来做。
<!-- ng-controller指令就是用来定义应用程序控制器对象的,同时也创建了一个新的作用域,就是那一对div--><div ng-app="" ng-controller="MyController"><p>请输入一个名字:<input type="text" ng-model="person.name"> </p><p>Hello <span ng-bind="person.name"></span> </p></div>
这样就能创建一个控制器,本来控制器是个JS对象,名字叫MyController,但我们这里没有为它编写任何代码,只是加上了ng-controller这个属性指令,所以会按默认情况被Angular创建。
这个MyController在这最大的意义就是划定了一个作用域,将它所在的div标签范围内都定义成了它自己的作用域,这意味着如果在这控制器的内部定义一个变量,比如上例中的person.name,它就只能在作用域内输出,离开这对div,就无法输出了。这样它不会和其它作用域的同名变量发生冲突。这样,我们就可以开始放心地在这个控制器内创建各种变量、函数了。
一个页面内可以有多个控制器作用域,还可以嵌套。不同作用域之间的变量,有各自的有效范围。
下面展示了控制器里最重要的一个属性$scope,它是当前控制器里用来容纳你的自定义变量和函数的上下文对象。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><!-- 没有再使用ng-init指令,而是通过一个自定义的控制器里的JS代码来实现同样的初始化功能--><div ng-app="" ng-controller="MyController"><p><span ng-bind="title"></span> </p><p>请输入一个名字:<input type="text" ng-model="person.name"> </p><p>Hello <span ng-bind="person.name"></span> </p></div><script><!-- 这个函数会在浏览器加载页面遇到ng-controller="MyController"指令时执行,它创建了一个$scope,MyController作用域的上下文对象,然后把这个作用域内会用到的所有数据、属性、方法,都放在了这个对象中-->function MyController($scope) {<!-- 定义了一个person对象,放在了$scope上下文对象里-->$scope.person = {name: "World"};<!-- 定义了一个title字符串,也放在了$scope上下文对象里 -->$scope.title = "我是MyController控制器上下文中名为title的变量值";}</script></body></html>

$scope是有继承特性的,每个指令标签都会创建一个作用域,一个$scope上下文对象,那如果当前作用域是一个子作用域(外层的HTML标签上还有别的Angular指令),那当前作用域$scope也就是外层作用域$scope的子$scope,这样如果你在当前$scope中调用某个属性或方法没有的时候,比如上例中的$scope.person,它会自动往父$scope里找,再找不到继续往上找,直到最顶级的$rootScope,这是定义了ng-app的那个作用域对应的$scope。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><!-- 没有再使用ng-init指令,而是通过一个自定义的控制器里的JS代码来实现同样的初始化功能--><div ng-app="" ng-init="parentName = 'app'"><div ng-controller="MyController"><p><span ng-bind="title"></span> </p><p>请输入一个名字:<input type="text" ng-model="person.name"> </p><p>Hello <span ng-bind="person.name"></span> </p><p>Hello <span ng-bind="parentName"></span> </p></div></div><script><!-- $scope是MyController作用域的上下文对象,这个作用域内的所有数据、属性,都可以放在这个对象中-->function MyController($scope) {<!-- 定义了一个person对象,放在了$scope上下文对象里-->$scope.person = {name: "World"};<!-- 定义了一个title字符串,也放在了$scope上下文对象里 -->$scope.title = "我是MyController控制器上下文中名为title的变量值";}</script></body></html>

<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="" ng-controller="MyController">Your name:<input type="text" ng-model="username"><!-- 点击这个按钮会调用MyController作用域$scope.sayHello()方法--><button ng-click="sayHello()">打招呼</button><hr>{{greeting}}</div><script>function MyController($scope) {$scope.username = 'World';<!-- 把sayHello声明成了一个方法 -->$scope.sayHello = function() {$scope.greeting= 'Hello ' + $scope.username + '!';};}</script></body></html>

当然,控制器写在外部文件里也是可以的。
<script src="MyController.js"></script>
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="" ng-init="status=false"><button ng-click="status= !status">我变</button><p ng-hide="status">显示了。</p><p ng-hide="!status">隐藏了。</p></div></body></html>
ng-hide之前我们已经见识过了,另外还有一个ng-show,作用是相反的。
但是,如果页面上绑定的数据过多,页面加载起来会很卡,不管你是不是用ng-hide把一部分藏起来,都是一样卡的,因为它还是在DOM里存在着。所以,应该尽量使用ng-if,它只有在为true的时候,才会去执行其中的数据绑定。既达到了隐藏效果,还能让页面飞快。
angular通常建议一个页面不要超过2000个用于数据绑定的model。
ng-repeat,遍历集合并渲染到页面上。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="" ng-init="friends = [{name:'Tom', age:25},{name:'Jerry', age:28},{name:'Tom', age:25},{name:'Jerry', age:28}]"><table><tbody><!-- 这里会重复输出4次这个tr,x是数组friends的元素,数组中每个元素都是一个对象,对象有两个属性name和age--><tr ng-repeat="x in friends"><td> {{ 'Name:'+ x.name +' ,Age:'+ x.age}} </td></tr></tbody></table></div></body></html>
也就是循环输出了。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="">请输入: <input type="text" ng-model="name"><!-- |后面是一个过滤器名称,uppercase过滤器是用于对字符小写转换成大写,还有许多别的过滤器,也可以自定义过滤器--><p>结果为: {{ name | uppercase}}</p></div></body></html>
过滤器可以对变量在输出前做额外操作。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="" ng-init="friends = [{name:'tom', age:16},{name:'jerry', age:20},{name:'garfield', age:22}]"><!-- 将过滤的字符存入text变量--><p>输入过滤:<input type="text" ng-model="text"></p><ul style="list-style-type:none"><li>姓名,年龄</li><!-- 在遍历时,用名为filter的过滤器进行筛选,只会显示输出中含有text字符的行,就像数据库like查询--><li ng-repeat="x in friends | filter:text">{{ x.name + ' , ' + x.age }}</li></ul></div></body></html>
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="" ><!-- 输入数值212432到num变量--><p>输入过滤:<input type="text" ng-model="num"></p><div ng-bind="num | currency"><!--输出的是$212,432.00--></div></div></body></html>
货币过滤器,通常会按所在地区显示格式。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="" ng-controller="MyController"><!-- 格式化输出日期--><div ng-bind="d | date:'yyyy-MM-dd HH:mm:ss:sss'"><!--输出的是2015-07-22 11:28:09:887--></div></div><script>function MyController($scope) {$scope.d = new Date();}</script></body></html>
日期过滤器。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="gbk"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="" ng-init="words = ['aaa','AAA']" ng-controller="MyController"><ul style="list-style-type:none"><li>姓名,年龄</li><!-- 在遍历时,用filter过滤器执行自定义过滤方法myFilter--><li ng-repeat="x in words | filter:myFilter">{{ x }}</li></ul></div><script>function MyController($scope) {$scope.myFilter = function(str) {<!-- 只有首字母为大写时返回true,这样遍历结果只会显示首字母为大写的元素-->return str[0] == str[0].toUpperCase();};}</script></body></html>
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="" ng-init="friends = [{name:'Tom', age:25},{name:'Jerry', age:28},{name:'Tom', age:25},{name:'Jerry', age:28}]"><div ng-bind="friends | json"><!--这个过滤器就是把js对象转成json字符串输出在页面上,输出的是[ { "name": "Tom", "age": 25 }, { "name": "Jerry", "age": 28 }, { "name": "Tom", "age": 25 }, { "name": "Jerry", "age": 28 } ]--></div></div></body></html>
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="" ><!--这个过滤器用来截取长度--><div ng-bind="'I am the King!' | limitTo:3"><!--输出I a--></div><div ng-bind="'I am the King!' | limitTo:-3"><!--输出ng!--></div><div ng-bind="['a','b','c'] | limitTo:2"><!--输出a b--></div></div></body></html>
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="" ><!--将数字转成文本--><div ng-bind="12345 | number"><!--输出I a--></div><div ng-bind="123.456 | number:2"><!--输出ng!--></div></div></body></html>
指令也是一种angular特定的对象,就像控制器一样,angular提供了一些内置指令,也可以开发自定义指令。
<!doctype html><html><head><script src="http://www.hubwiz.com/scripts/angular.min.js">></script></head><body ><div ng-app="" ng-init="dstatus = true; rstatus = true"><h1>Demo 1:</h1><button ng-click="dstatus = !dstatus">改变禁用状态</button><button ng-click="rstatus = !rstatus">按钮改变只读状态</button><h1>Demo 2:</h1><!-- 动态决定disabled状态-->><textarea ng-disabled="dstatus" ng-readonly="rstatus"></textarea></div></body></html>
上例的ng-click和ng-disabled,ng-readonly都是内置指令,类似的还有ng-checked和ng-selected。
指令也分类型,ng-disabled,ng-readonly,ng-checked,ng-selected这些是布尔型指令,就简单对应HTML原有的属性标签控制true或false。
还有两个是类布尔型的,就是说它在HTML中不是布尔型,但由于行为相似,angular是把它们当成布尔型指令的。如ng-href,ng-src。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="gbk"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><div ng-app="myApp" ><!-- 用普通的href要是还没加载完你就点了,404... --><a href="{{ myHref }}">一个链接</a><!-- 用ng-href,在你的链接加载出来前你的点击是不会跳转的,会等到加载完毕以后 --><a ng-href="{{ myHref }}">一个链接</a><!-- 加载出来之前会是个XX--><h1>错误方法</h1><img src="{{imgSrc}}"/><!-- 页面会延迟加载图象--><h1>正确方式</h1><img ng-src="{{imgSrc}}"/><!-- 其实很多现代浏览器已经避免了在imgSrc没值时出现X,用哪个效果都一样了--><script><!-- 我们延迟两秒看看效果-->angular.module('myApp', []).run(function($rootScope, $timeout) {$timeout(function() {$rootScope.myHref = 'http://www.baidu.com';$rootScope.imgSrc = 'https://ss1.baidu.com/9vo3dSag_xI4khGko9WTAnF6hhy/super/pic/item/d31b0ef41bd5ad6e9463d6f787cb39dbb6fd3c10.jpg';}, 2000);});</script></div></body></html>
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><!-- 没有再使用ng-init指令,而是通过一个自定义的控制器来实现同样的初始化功能--><div ng-app="" ng-controller="MyController"><p>请输入一个名字:<input type="text" ng-model="person.name"> </p><p>Hello <span ng-bind="person.name"></span> </p></div><script><!-- $scope是MyController作用域的上下文对象,这个作用域内的所有数据、属性,都可以放在这个对象中-->function MyController($scope) {$scope.person = {name: "World"};}</script></body></html>
我们之前为了实现控制器,把Controller写成了一个js函数,这样定义在全局命名空间的函数越来越多以后,js管理起来会比较容易冲突和混乱。所以angular建议将这些自定义的js内容写在模块里。
angular.module('myApp', []);定义了一个模块。第一个参数是模块名,第二个数组参数里可以放这个模块中要注入的对象。
angular.module('myApp')获取模块。
接下来用模块的写法改造上例。
<!DOCTYPE HTML><html><head><title> 测试页 </title><meta charset="utf-8"><script src="http://www.hubwiz.com/scripts/angular.min.js"></script></head><body><!-- 这里的ng-app不再是空名字,而是加上了模块名myApp--><div ng-app="myApp" ng-controller="MyController"><p>请输入一个名字:<input type="text" ng-model="person.name"> </p><p>Hello <span ng-bind="person.name"></span> </p></div><script><!-- 这里将模块名定义成myApp,然后通过.controller增加了一个新的MyController到我们定义的模块里-->angular.module('myApp', []).controller('MyController', function($scope) {$scope.person = {name: "World"};});<!-- 为了便于部分人的理解,上面的代码还可以这么写-->angular.module('myApp', []); // 定义模块var myModule = angular.module("myApp"); // 按名称获取模块// 往模块里定义ControllermyModule.controller('MyController', function($scope) {$scope.person = {name: "World"};});</script></body></html>
现在你可以展开想象这些js代码如何拆分管理存放了。
模块不仅可以通过.controller定义控制器,还可以用.directive定义自定义指令,用.factory定义服务等等,用法大致相同,都是第一个参数传名称,第二个参数放函数,然后不传第二个就是获取。
模块结合前面内容的综合例子:
<div ng-controller="SomeController">{{ someModel.someProperty }}<button ng-click="someAction()">改值</button></div><script>angular.module('myApp', []).controller('SomeController', function($scope) {// 给$scope添加一个模型实体属性$scope.someModel = {someProperty: 'hello computer'}// 给$scope添加一个方法,可以修改模型的值$scope.someAction = function() {$scope.someModel.someProperty = 'hello human';};});</script>