正在写一个用到 AngularFire
的博客项目,需要一个 分页器
。
在 AngularFire
官方文档里, Firebase
的 Query API
实在太少。
官方提供了一个 Paginator
示例,但是因为进行偏移查询的时候必须指定开始位置记录的 key
,所以每一次读取都要多读一条。
(现在想想虽然可以在循环的时候跳过第一条,但是)当时觉得 $firebaseArray
又没有提供 slice
函数,完全不知道怎么样截取所需片段,所以还是自己来写一个吧。
下面是代码,为了避免有同学复制代码之后需要花半天时间删掉注释,所以代码分析写在上面。
在 BlogListCtrl
里面, $scope.blogs
从 BlogService
获取所有博客数据。
因为用的是 firebase
,所以一定不能破坏最宝贵的实时性, $scope.blogs
是一个 $firebaseArray
。
$scope.current_page
,当前页码。
$scope.count
,文章总数。
$scope.perpage
,每一页显示的文章数量。
$scope.count = $scope.blogs.length;
:来自 firebase
的数据是实时加载的,在代码执行到某个位置的时候,数据可能还没传输到变量中。此时 $scope.blogs
为空, length
必然也不存在。为了保证获得数据后立刻设置正确的值,所以写在 $loaded()
里面。
$scope.totalPage
,计算总页数,应该很好理解,里面还有一行注释:)。
$scope.currentN
,当前页的文章数量。
$scope.hasPrev
和 scope.hasNext
,判断当前页是否有前后页。
$scope.prevPage
和 scope.nextPage
,修改页码为上一页或者下一页。
BlogListCtrl
fireblogControllers.controller('BlogListCtrl', ['$scope', "BlogService", "OptionService", function ($scope, BlogService, OptionService){ $scope.current_page = 1; $scope.count = 0; $scope.perpage = 10; $scope.blogs = BlogService.getAll(); $scope.blogs.$loaded().then(function () { //to make sure that $scope.blogs is already loaded, otherwise length doesn't exist $scope.count = $scope.blogs.length; }); $scope.totalPage = function() { //if $scope.count==0, means at this moment the data hasn't been loaded. return $scope.count==0?1:Math.ceil($scope.count/$scope.perpage); } //currentN is the number of articles of the current page. $scope.currentN = function() { var n = $scope.count - ($scope.current_page-1)*$scope.perpage; return n>=10?10:n; } $scope.hasPrev = function() { return $scope.current_page==1?false:true; } $scope.hasNext = function() { return $scope.totalPage()==$scope.current_page?false:true; } $scope.prevPage = function() { if(($scope.current_page--)<1){ $scope.current_page = 1; } } $scope.nextPage = function() { if(($scope.current_page++)>$scope.totalPage()){ $scope.current_page = $scope.totalPage(); } } $scope.range = function(n) { return new Array(n); }; var page_name = "HOME"; var site_name = OptionService.setSiteTitle(page_name); } ]);
在 BlogService
里面,用到的 cacheData
来缓存数据。其他的没什么好说的。
BlogService
fireblogServices.factory("BlogService", ["$firebaseArray", "$firebaseObject", function($firebaseArray, $firebaseObject) { var cacheData; function getAll(){ if(cacheData){ return cacheData; }else{ var ref = new Firebase("https://github-pages.firebaseio.com/blogs"); cacheData = $firebaseArray(ref); return cacheData; } } function getOne(id){ var ref = new Firebase("https://github-pages.firebaseio.com/blogs/"+id); return $firebaseObject(ref); } return { getAll: function() { return getAll(); }, getRecord: function(id) { return getAll().$getRecord(id); }, getObjectByID: function(id) { return getOne(id); }, }; } ]);
在 BlogService
里面,用到的 cacheData
来缓存数据。其他的没什么好说的。
ng-repeat="i in range(currentN()) track by $index”
,这是一个很有趣的东西。
我之前是用 ng-repeat=“blog in blogs”
,但是要分页显示,就只能显示一个子集。但是之前提到过,我没有找到切割的函数。
然后我想到可不可以指定循环次数,于是 google: angularjs ng-repeat 10 times
,找到了这个方法 ng-repeat=“i in range(10) track by $index”
, range(10)
是在 controller
里定义的一个获取数字数组的函数。
但是因为第一页和最后一页可能文章数量少于每页可以显示的文章数量,所以循环固定次数就会输出多几个没有数据的 html
片段。
最后决定用 currentN()
这个函数来获取当前页面文章数。
$index+(current_page-1)*10
应该比较好理解。
ng-if=“hasPrev()”
,当存在上一页的时候显示。
ng-if=“hasNext()”
当存在下一页的时候显示。
ng-click=“prevPage()”
,将 a
标签绑定换页函数。
ng-click=“nextPage()”
将 a
标签绑定换页函数。
blog-list.html
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1"> <div class="post-preview" ng-repeat="i in range(currentN()) track by $index"> <a href="#/p={{blogs[$index+(current_page-1)*10].$id}}"> <h2 class="post-title"> {{blogs[$index+(current_page-1)*10].title}} </h2> <h3 class="post-subtitle"> {{blogs[$index+(current_page-1)*10].description}} </h3> </a> <p class="post-meta">发布于 {{blogs[$index+(current_page-1)*10].date | date:"MM/dd/yyyy @ h:mma"}}</p> </div> <!-- Pager --> <ul class="pager"> <li class="previous" ng-if="hasPrev()"> <a href="" ng-click="prevPage()">← Previous Posts</a> </li> <li class="next" ng-if="hasNext()"> <a href="" ng-click="nextPage()">Older Posts →</a> </li> </ul> </div>
总结:
优点:不用重新加载数据,不用加载多余数据(相对于官方例子)。
缺点:第一次加载全部数据,如果数据量大的话就是负担了。
希望对大家有帮助。
以上。
2016.03.02
因为 firebase
没法倒序查询,所以输出博客列表的时候需要自己实现倒序输出。
也很简单,就是把 blogs[$index+(current_page-1)*10]
改成 blogs[count-1-($index+(current_page-1)*10)]
。