工作区切换到步骤6
在这一步骤中,我们将为程序添加一个特性使得可以对手机的显示排序。这还需要我们为手机数据模型中添加一些新信息作为排序的依据,并以此写出转换器的处理让数据起作用使得程序达到预期效果。
程序现在除了有一个搜索框还有一个下拉选择框,它允许选择数据排序的要求。
下面看一下我们的模板发生了上面样的变化
phone-list.template.html
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<!--Sidebar content-->
<p>
Search:
<input ng-model="$ctrl.query" />
</p>
<p>
Sort by:
<select ng-model="$ctrl.orderProp">
<option value="name">Alphabetical</option>
<option value="age">Newest</option>
</select>
</p>
</div>
<div class="col-md-10">
<!--Body content-->
<ul class="phones">
<li ng-repeat="phone in $ctrl.phones | filter:$ctrl.query | orderBy:$ctrl.orderProp">
<span>{{phone.name}}</span>
<p>{{phone.snippet}}</p>
</li>
</ul>
</div>
</div>
</div>
首先,我们添加了一个<select>的html元素,并命名为orderProp,这样我们就有了两个排序依据选择
然后我们链接了经过filter转换器过滤后的数据作为orderBy转换器处理的输入。orderBy转换器会依据规则对输入的数据(数组)排序,即输出一个排序后的新数据(数组形式)。
在这里,Angular在select元素和orderProp数据模型间创建了一个双向数据绑定,然后orderProp被用于orderBy转换器(过滤器)。
正如我们在步骤3中看到的,因为这样的数据绑定使得任何的数据变化都可以及时的反映到输出结果中(这里是通过下拉菜单对排序条件的改变),而且这一过程中没有特别指定复杂的DOM操作处理(没有专门写这方面的代码)就自动实现了效果,这就是Angular数据绑定的威力所在。
控制器
phone-list.component.js
'use strict';
// Register `phoneList` component, along with its associated controller and template
angular.
module('phoneList').
component('phoneList', {
templateUrl: 'phone-list/phone-list.template.html',
controller: function PhoneListController() {
this.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
}
];
this.orderProp = 'age';
}
});
我们编辑了phones的数据模型,即手机数组,在这个结构对每条记录中增加了age元素项,其可用于按手机推出时间排序。
我们在控制器中设置了默认的值作为排序依据,即设置了orderProp的值为 age。如果不在这里设置这个值,则orderBy转换器(过滤器)是没有初始化的,直到我们在页面下拉菜单中进行了选择为止。
现在可以好好来谈谈双向数据绑定了。注意,当浏览器加载完成程序后下拉菜单的Newest项目是被选中的,这就是因为我们在控制器中设置了orderProp的排序依据是age,所以发生了数据模型向UI的绑定,现在我们在下拉菜单选择Alphabetically项,则数据模型马上会自动更新,让手机列表的排序依据要求发生变化,这时的数据绑定方向与前面恰好相反,是UI向数据模型的。
步骤7——XHRs与依赖注入
显然,一个足够强大的程序仅仅依靠硬编码的3条手机数据设置是不行的。这里我们将利用Angular内置的叫做$http的服务功能,从web服务器获取一个巨大的数据表。我们还将使用到Angular的依赖注入(dependency injection)向PhoneListCtrl控制器提供服务。
现在有20条手机信息了,而且这些信息是通过服务器加载的。
工作区切换到步骤7
数据
在项目app/phones/phones.json文件中有一个巨大的列表,,是采用JSON格式组织的手机信息数据。文件中,它们大概是如下的形式:
[
{
"age": 13,
"id": "motorola-defy-with-motoblur",
"name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
"snippet": "Are you ready for everything life throws your way?"
...
},
...
]
控制器
这里的控制器中,我们使用了Angular的$http服务,利用一个HTTP请求,从服务器中获取到app/phones/phones.json文件数据。$http是Angular内建web程序通用服务(功能)中的一个,Angular会在程序需要时自动注入这些服务功能。
这些服务由Angular依赖注入子系统进行管理。依赖注入帮助你的程序在好的结构(例如独立的数据、控制和表现/展示)和松耦合(组件间解耦,组件之间的依赖关系不由组件自身确定,而由依赖管理子系统协调)。phone-list.component.js:
'use strict';
// Register `phoneList` component, along with its associated controller and template
angular.
module('phoneList').
component('phoneList', {
templateUrl: 'phone-list/phone-list.template.html',
controller: function PhoneListController($http) {
var self = this;
self.orderProp = 'age';
$http.get('phones/phones.json').then(function(response) {
self.phones = response.data;
});
}
});
$http发起一个HTTP GET请求,其内容是请求web服务器端的phones/phones.json文件(这里的URL是相对于index.html的相对路径)。服务器端响应这个请求,提供了json文件的内容(响应其实是一个后台服务中动态处理的反馈结果,这对于浏览器或者我们的程序来说这看起来是相同的/透明的。这个教程中为了简单起便,直接是一个json数据文件了。)
$http服务得到了一个有success方法的promise object,然后我们就可以调用方法来处理异步响应和分配手机数据来构建我们控制器中作用范围中的phones数据了。注意,这里Angular自动检测了json类型响应,并分析结构化了数据。
为了使用Angular服务,你只用在控制器中需要的地方简单把调用名字作为构造函数的参数,例如:
phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {...}
Angular的依赖注入管理会在控制器初始化时自动的提供声明的功能,而且依赖注入管理还自动的处理相应的层次依赖关系(通常一个服务功能还取决于其他服务功能,这些问题Angular都会自动处理)。
注意参数的名字很重要(不能随意变动),因为依赖管理会用到这些名字进行查找来解决依赖关系并进行注入。
$前缀名的约定
你可以创建自己的服务,事实上我们在步骤11中就会这样做。作为一个名称方面的约定,Angular内置的服务,作用范围方法和一些其它的Angular的API有一个$前缀。
这样有$前缀的都被用于了Angular预定服务,为了防止名称方面的冲突,你在定义服务和数据模型时最好都不要用$作为前缀。
在检查代码时你可能会注意到有些作用范围内的数据定义是$$来开始的,这意味这这些内容是私人的(该受保护的),你不该在外部进行访问或修改。