用于 AngularFire 项目的分页器

Paginator for AngularFire

正在写一个用到AngularFire的博客项目,需要一个分页器

AngularFire官方文档里,FirebaseQuery API实在太少。

官方提供了一个Paginator示例,但是因为进行偏移查询的时候必须指定开始位置记录的key,所以每一次读取都要多读一条。

(现在想想虽然可以在循环的时候跳过第一条,但是)当时觉得$firebaseArray又没有提供slice函数,完全不知道怎么样截取所需片段,所以还是自己来写一个吧。

下面是代码,为了避免有同学复制代码之后需要花半天时间删掉注释,所以代码分析写在上面。

BlogListCtrl里面,$scope.blogsBlogService获取所有博客数据。

因为用的是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.hasPrevscope.hasNext,判断当前页是否有前后页。

  • $scope.prevPagescope.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 &rarr;</a>
        </li>
    </ul>
</div>

总结:

  • 优点:不用重新加载数据,不用加载多余数据(相对于官方例子)。

  • 缺点:第一次加载全部数据,如果数据量大的话就是负担了。

希望对大家有帮助。

以上。

2016.03.02
因为firebase没法倒序查询,所以输出博客列表的时候需要自己实现倒序输出。
也很简单,就是把blogs[$index+(current_page-1)*10]改成blogs[count-1-($index+(current_page-1)*10)]

    原文作者:CHENGKANG
    原文地址: https://segmentfault.com/a/1190000004521156
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞