0

AngularJS框架 如何进行 双向数据绑定

上一篇AngularJS文章中,我们简单的了解,并且动手写出了AngularJS框架的Helloworld应用,今天我们来学习第二个案例:AngularJS框架是如何进行双向数据绑定的。


先看如下的AngularJS代码:


<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>


这是一个非常普通的HTML页面,在这个例子中除了上一篇文章中介绍的ng-app、ng-controller以外,这里还多了一个ng-repeat,其实这个就是列表标签符号,类似Java SSH框架Struts的logic:iterator之类。这个标签是要告诉AngularJS为列表每一行创建一个 <li> 开头的元素。


运行之后显示效果如下:



AngularJS控制器代码如下:


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">中定义启动的。

如下图:


AngularJS作用域scope



AngularJS中的一个ng-app就拥有一个注入器injector,它负责组件的获取和其中的依赖注入,类似Java SSH框架Spring的一个依赖注入框架或容器,每一个注册在injector中的组件都有一个名称,我们通过$injector,get(名称)来获得这个组件。比如这个案例名称是'PhoneListCtrl',首先从缓存寻找如果哦没有从工厂中创建新的。


模块'phonecatApp可以认为是提供注入器的管道,或者是注入器的工厂。


AngularJS有一重要的概念:胶水Scope对象

注意到上面第三行控制器的代码:


phonecatApp.controller('PhoneListCtrl', function ($scope)


$scope是注入到我们的控制器中,这个scope是root scope的原型对象。

作用域scope在AngularJS中很重要,作用域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来显露当前模型状态的改变。


我们接着看下面AngularJS例子的扩展


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框架的依赖注入模式:IOC控制反转模式。

上一篇 下一篇

相关文章