AngularJS 新手指南4

自定义过滤器

创建 phonecatFilters 模块,用此模块注册自定义过滤器:

1
2
3
4
5
angular.module('phonecatFilters', []).filter('checkmark', function() {
return function(input) {
return input ? '\u2713' : '\u2718';
};
});

过滤器的名字是“checkmark”,根据 input 判断 true 或 false,我们用 unicode 字符代替 true 或 false(\u2713 和 \u2718)。

我们需要给 phonecat 模块注册 phonecatFilters 模块。

1
angular.module('phonecat', ['phonecatFilters']).

模板

我们需要引入 app/js/filters.js 文件

1
2
<script src="js/controllers.js"></script>
<script src="js/filters.js"></script>

Angular 模版使用过滤器的语法如下:

{{ expression | filter }}

我们在手机详情模版使用此过滤器:

1
2
3
4
5
6
<dl>
<dt>Infrared</dt>
<dd>&#123;&#123;phone.connectivity.infrared | checkmark}}</dd>
<dt>GPS</dt>
<dd>&#123;&#123;phone.connectivity.gps | checkmark}}</dd>
</dl>

实验

  • 我们实验下 Angular 内建过滤器,在 index.html 添加以下绑定:

{{ “lower cap string” | uppercase }}
{{ {foo: “bar”, baz: 23} | json }}
{{ 1304375948024 | date }}
{{ 1304375948024 | date:”MM/dd/yyyy @ h:mma” }}

  • 我们也可以创建带有输入元素的模型,把过滤器绑定和它联合起来。index.html 加以下代码试试:
1
<input ng-model="userInput"> Uppercased: &#123;&#123; userInput | uppercase }}

事件处理

在手机详情页加个点击缩缩图切换对应大图的效果。

控制器

1
2
3
4
5
6
7
8
9
10
11
12
function PhoneDetailCtrl($scope, $routeParams, $http) {
$http.get('phones/' + $routeParams.phoneId + '.json').success(function(data) {
$scope.phone = data;
$scope.mainImageUrl = data.images[0];
});
$scope.setImage = function(imageUrl) {
$scope.mainImageUrl = imageUrl;
}
}
//PhoneDetailCtrl.$inject = ['$scope', '$routeParams', '$http'];

PhoneDetailCtrl 控制器,创建了 mainImageUrl 模型属性,默认值设为第一个手机图片的 URL。
我们也创建了 setImage 事件处理器,它将改变 mainImageUrl 的值。

模版

1
2
3
4
5
6
7
<img ng-src="\{\{mainImageUrl}}" class="phone">
<ul class="phone-thumbs">
<li ng-repeat="img in phone.images">
<img ng-src="\{\{img}}" ng-click="setImage(img)">
</li>
</ul>

我们给大图绑定 ngSrc 指令,设为 mainImageUrl 属性,也给缩略图注册了 ngClick 事件。当点击缩略图时,setImage 事件处理器会把 mainImageUrl 属性的值改成缩略图的 URL 。

实验

让我们往 PhoneDetailCtrl 添加一个新的控制器方法:

1
2
3
$scope.hello = function(name) {
alert('Hello ' + (name || 'world') + '!');
}

然后往 phone-details.html 模版添加以下代码:

1
<button ng-click="hello('Elmo')">Hello</button>

REST 和自定义服务

最后为应用定义一个基于 REST 客户端的自定义服务,用此客户端我们可以更容易的发起 XHR 请求获取数据,不必使用底层的 $http API,HTTP 方法和 URL。

模版

自定义服务定义到 app/js/services.js 中,我们把它引入到布局模版中。此外,需要加载 angular-resource.js 文件,此文件的 $resource 服务包含 ngResource 模块,我们马上要用:

1
2
<script src="js/services.js"></script>
<script src="lib/angular/angular-resource.js"></script>

服务

1
2
3
4
5
6
angular.module('phonecatServices', ['ngResource']).
factory('Phone', function($resource){
return $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
});

我们使用模块 API 注册自定义服务,用到了 factory(工厂) 函数。把 ‘Phone’ 服务名传到工厂函数里,工厂函数很像控制器的构造函数,都可以通过函数参数声明依赖。Phone 服务依赖于 $resource 服务。

$resource 服务几行代码就能创建一个 REST 客户端,此客户端可以取代底层的 $http 服务。

1
angular.module('phonecat', ['phonecatFilters', 'phonecatServices']).

我们需要给 ‘phonecat’ 应用添加数组形式的 ‘phonecatServices’。

控制器

我们简化了子控制器(PhoneListCtrl 和 PhoneDetailCtrl),用新的服务 Phone 取代底层的 $http 服务。

Angular 的 $resource 服务比 $http 更易用,代码也更容易理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function PhoneListCtrl($scope, Phone) {
$scope.phones = Phone.query();
$scope.orderProp = 'age';
}
//PhoneListCtrl.$inject = ['$scope', 'Phone'];
function PhoneDetailCtrl($scope, $routeParams, Phone) {
$scope.phone = Phone.get({phoneId: $routeParams.phoneId}, function(phone) {
$scope.mainImageUrl = phone.images[0];
});
$scope.setImage = function(imageUrl) {
$scope.mainImageUrl = imageUrl;
}
}
//PhoneDetailCtrl.$inject = ['$scope', '$routeParams', 'Phone'];

注意我们把:

1
2
3
$http.get('phones/phones.json').success(function(data) {
$scope.phones = data;
});

换成了:

1
$scope.phones = Phone.query();

非常值得注意的是,当调用 Phone 服务时, 我们没有传递回调函数。

结语

我们的应用完成啦,git checkout 命令可以跳回原先的步骤。

开发者指南 有更多的例子和详细资料。

更多代码实例,看 Cookbook

当你准备用 Angular 开发项目时,推荐你使用 angular 种子 引导开发。