AngularJS教程之二:双向数据绑定
前面我们简单了解了AngularJS的Helloworld应用,下面看看第二个案例:
- <html ng-app="phonecatApp">
- <head>
- ...
- <script src="lib/angular/angular.js"></script>
- <script src="js/controllers.js"></script>
- </head>
- <body ng-controller="PhoneListCtrl">
- <ul>
- <li ng-repeat="phone in phones">
- {{phone.name}}
- <p>{{phone.snippet}}</p>
- </li>
- </ul>
- </body>
- </html>
这个案例中除了前面介绍的ng-app ng-controller以外,多了一个ng-repeat,其实这就是一个列表标签符号,类似Struts的logic:iterator之类。告诉Angular为列表每一行创建一个 <li> 开头的元素。
显示效果如下:
控制器代码如下:
- var phonecatApp = angular.module('phonecatApp', []);
- phonecatApp.controller('PhoneListCtrl', function ($scope) {
- $scope.phones = [
- {'name': 'Nexus S',
- 'snippet': 'Fast just got faster with Nexus S.'},
- {'name': 'Motorola XOOM™ with Wi-Fi',
- 'snippet': 'The Next, Next Generation tablet.'},
- {'name': 'MOTOROLA XOOM™',
- 'snippet': 'The Next, Next Generation tablet.'}
- ];
- });
这里我们为作用域$scope定义了一个phones 列表数组,分别可以用{{phone.name}}和{{phone.snippet}}取出。
不知道有没有注意到,这个案例和第一个Helloworld案例 区别在于第一行代码。这里不是一个简单的自己定义的JS函数,而是从一个模块'phonecatApp'中获得,获得以后,声明为'PhoneListCtrl'.而这个模块是在模板<html ng-app="phonecatApp">中定义启动的。如下图:
一个ng-app拥有一个注入器injector,负责一群组件的获取和其中依赖注入,类似一个依赖注入框架或容器,每一个注册在injector中的组件都有一个名称,我们通过$injector,get(名称)来获得这个组件。比如这个案例名称是'PhoneListCtrl'。首先从缓存寻找如果哦没有从工厂中创建新的。
模块'phonecatApp可以认为是提供注入器的管道,或者是注入器的工厂。
胶水Scope对象
注意到上面第三行控制器的代码:phonecatApp.controller('PhoneListCtrl', function ($scope)
$scope是注入到我们的控制器中,这个scope是root scope的原型对象。
作用域scope在Angular中很重要,作用域scope可以被看作是胶水,允许模板,模型和控制器一起工作。
如下图:scope is the gule表示scope就是胶水。
$scope对象中增加的字段name和方法action,对应到视图Dom中的{{name}}和action();
下图是scope和模型之间的对应关系。
scope中的name对应模型字符串,value数值型对应number,原始类型对应;对象hash也是对应映射;类型Type对应映射。
所有对模型的改变都会通过scope同步映射到视图View,保持模型和视图数据的同步变化,同样视图的任何变化将映射到模型中。下图表示这种Model和视图DOM之间随时随地的同步映射:
下面通过案例说明这种同步实时更新的魅力。
ng-repeat
注意到 <li ng-repeat="phone in phones">写法,我们还可以有
<table>
<tr><th>row number</th></tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr>
</table>
<table>
<tr><th>row number</th></tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table>
下面我们为这个repeat增加过滤器。
- Search: <input ng-model="query">
- <li ng-repeat="phone in phones | filter:query">
- {{phone.name}}
- <p>{{phone.snippet}}</p>
注意到我们使用"|"来实现过滤器。AngularJS过滤器用来格式化输出给用户的数据。除了格式化数据,过滤器还能修改DOM。这使得过滤器通常用来做些如“适时地给输出加入CSS样式”等工作。
name | uppercase
是一个大写格式化的过滤器。
上面这个案例的过滤器是:这可以让用户输入搜索条件,并立即看到自己输入的搜索词语对下面手机清单上的影响。如果输入m,将在清单中只出现m开始的字母:
这里很好体现了通过scope实现界面视图和Model模型的双向同步绑定,用户在名为query的输入框中输入数据后,立即通过列表repeater的过滤器 (phone in phones | filter:query)显示出来,数据模型的改变立即影响到repeater的改变,而repeater更新DOM来显露当前模型状态的改变。
下面再看看这个案例的扩展
Search: <input ng-model="query">
Sort by:
<select ng-model="orderProp">
<option value="name">Alphabetical</option>
<option value="age">Newest</option>
</select>
<ul class="phones">
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp">
{{phone.name}}
<p>{{phone.snippet}}</p>
</li>
</ul>
这个案例中我们增加了select ng-model="orderProp",这个应该是模型中的一个对象,过滤器中增加了orderBy:根据orderProp的值排序,如果用户在下拉菜单选择Newest,那么orderProp值是age,根据年龄排序;如果选择Alphabetical,那么orderProp值是Alphabetical,根据字母排序。
var phonecatApp = angular.module('phonecatApp', []);
phonecatApp.controller('PhoneListCtrl', function ($scope) {
$scope.phones = [
{'name': 'Nexus S',
'snippet': 'Fast just got faster with Nexus S.',
'age': 1},
{'name': 'Motorola XOOM? with Wi-Fi',
'snippet': 'The Next, Next Generation tablet.',
'age': 2},
{'name': 'MOTOROLA XOOM?',
'snippet': 'The Next, Next Generation tablet.',
'age': 3}
];
$scope.orderProp = 'age';
});
在控制器中设定orderProp的初始缺省是根据age排序。phones数组中多了age字段。