@iamfox
2015-10-19T20:07:29.000000Z
字数 15096
阅读 1867
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"); // 按名称获取模块
// 往模块里定义Controller
myModule.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>