AngularJS教程之二:双向数据绑定

上页

  前面我们简单了解了AngularJS的Helloworld应用,下面看看第二个案例:

  1. <html ng-app="phonecatApp">
  2. <head>
  3. ...
  4.   <script src="lib/angular/angular.js"></script>
  5.   <script src="js/controllers.js"></script>
  6. </head>
  7. <body ng-controller="PhoneListCtrl">
  8.  
  9. <ul>
  10. <li ng-repeat="phone in phones">
  11.   {{phone.name}}
  12.   <p>{{phone.snippet}}</p>
  13. </li>
  14. </ul>
  15.  
  16. </body>
  17. </html>

  这个案例中除了前面介绍的ng-app ng-controller以外,多了一个ng-repeat,其实这就是一个列表标签符号,类似Struts的logic:iterator之类。告诉Angular为列表每一行创建一个 <li> 开头的元素。

显示效果如下:

控制器代码如下:

  1. var phonecatApp = angular.module('phonecatApp', []);
  2.  
  3. phonecatApp.controller('PhoneListCtrl', function ($scope) {
  4. $scope.phones = [
  5.   {'name': 'Nexus S',
  6.   'snippet': 'Fast just got faster with Nexus S.'},
  7.   {'name': 'Motorola XOOM™ with Wi-Fi',
  8.   'snippet': 'The Next, Next Generation tablet.'},
  9.   {'name': 'MOTOROLA XOOM™',
  10.   'snippet': 'The Next, Next Generation tablet.'}
  11. ];
  12. });

  这里我们为作用域$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增加过滤器。

 

  1. Search: <input ng-model="query">
  2. <li ng-repeat="phone in phones | filter:query">
  3.     {{phone.name}}
  4.     <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字段。

效果演示:http://angular.github.io/angular-phonecat/step-4/app/

下页

依赖注入

AngularJS专辑