对于大多数web应用来说显示项目列表是一种很常见的任务。通常情况下,我们的数据会比较多,无法很好地显示在单个页面中。在这种情况下,我们需要把数据以页的方式来展示,同时带有转到上一页和下一页的功能。既然在整个应用中这是一种很常见的需求,那么把这一功能抽象成一个通用的、可复用的分页(Paginator)服务是很有意义的。
Paginator服务(一个很简单的实现)应该允许用户告诉它如何获取数据,可以给定offset参数、limit参数,以及每页的数据条数。然后它将会在内部处理以下逻辑:获取哪些数据?下一页是什么?是否存在下一页?等等。
实例所提供的实现方式是:把currentPageItems字段存储在一个缓存中,如果可用的话就从这个字段里获取数据,否则就调用fetchFunction函数。当然,此服务还可以进一步扩展,实现在服务内部缓存数据。
service.js
angular.module('services', []).factory('Paginator', function() { //虽然这是一个服务,但是每次用户调用它时都会获得一个全新的Paginator。这是因为我们会返回一个function,当它被执行时会返回一个对象 return function(fetchFunction, pageSize) { var paginator = { hasNextVar : false, next: function() { if(this.hasNextVar) { this.currentOffset += pageSize; this._load(); } }, _load: function() { var self = this; fetchFunction(this.currentOffset, pageSize+1, function(items) { self.currentPageItems = items.slice(0, pageSize); self.hasNextVar = items.length === pageSize + 1; }); }, hasNext: function() { return this.hasNextVar; }, previous: function() { if(this.hasPrevious()) { this.currentOffset -= pageSize; this._load(); } }, hasPrevious: function() { return this.currentOffset !== 0; },currentPageItems: [], currentOffset:0 }; //加载第一页 paginator._load(); return paginator; }; });
在调用Paginator服务的时候,需要两个参数:fetch函数和每一页的大小。fetch函数所期望的签名如下:
fetchFunction(offset, limit, callback);
当Paginator需要加载并显示一个特定的页面时,就会调用此函数,同时向它传递正确的offset和limit参数。在这个函数的内部,它可以从一个很大的数组中截取数据,也可以调用服务发起一次加载数据的操作。当数据可用时,fetch函数会调用callback函数,并把数据传给它。
Paginator暴露了它上面的currentPageItems属性,这个属性可以绑定到模板中的迭代器上(也可以使用其他展示数据的方式)。hasNext()和hasPrevious()可以用来计算何时显示Next和Previous Page链接,当点击链接时,它需要根据情况调用next()或者previous()函数。
如果需要从服务器上加载一页数据,那么应该使用以上服务呢?以下是一种可能的控制器,它每次从服务端获取搜索之后的一页数据,示例如下:
controller.js
var app = angular.module('myApp', ['myApp.services']); app.controller('MainCtrl', ['$scope', '$http', 'Paginator', function($scope, $http, Paginator) { $scope.query = 'Testing'; var fetchFunction = function(offset, limit, callback) { $http.get('/search', {params:{query:$scope.query, offset: offset, limit: limit}}).success(callback); }; $scope.searchPaginator = Paginator(fetchFunction, 10); }]);
使用Pagination服务的HTML页面如下:
index.html
<!DOCTYPE html> <html ng-app="myApp"> <head lang="en"> <meta charset="utf-8"></meta> <title>Pagination Service</title> <script src="angular/angular.js"></script> <script src="service.js"></script> <script src="controller.js"></script> </head> <body ng-controller="MainCtrl"> <input type="text" ng-model="query"></input> <ul> <li ng-repeat="item in searchPaginator.currentPageItems"> {{item}} </li> </ul> <a href="" ng-click="searchPaginator.previous()" ng-show="searchPaginator.hasPrevious()"><<Prev</a> <a href="" ng-click="searchPaginator.next()" ng-show="searchPaginator.hasNext()">Next >></a> </body> </html>