nodejs分页设计配合bootstrap-paginator

从零开始nodejs系列文章

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者
张丹(Conan), 程序员Java,R,PHP,Javascript
weibo:@Conan_Z
blog: http://blog.fens.me
email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-bootstrap-paginator/

前言

分页功能是查询的必备功能,比如我要查询一个多于1000条记录的结果集。如果一次全部展示,不仅浪费带宽,增长等待时间,用户看到这么多的数据也会觉得的头疼。但是有了分页功能,可以根据用户的习惯,一页一页的处理少量的数据,展示少量的数据,就像是在看书一样的。

分页功能非常实用,但是实现起来也稍有复杂。不仅要考虑到前端的展示和传值问题,还要考虑后端面向数据的查询语句。不过我也发现了前端和后端对应的解决的方案。前端程序可以通过bootstrap-paginator项目来封装,而后端我是自己实现的。本来是准备使用mongoose-paginate项目,但他的设计过于简单,而且与bootstrap-paginator的一些传参习惯不符,自已实现其实更好控制。

page

目录

  1. 分页前端雏形bootstrap-paginator
  2. 分页后端查询mongoose
  3. 分页结果返回页面(后端->前端)
  4. 分页查询参数传递(前端->后端)
  5. 分页完整展现

 

1. 分页前端雏形bootstrap-paginator

下载bootstrap-paginator:下载地址
解压后,把bootstrap-paginator.min.js文件复制到public/js的目录

如果你的项目,已经配置了bootstrap, jquery。 那么你只需要在用到分页的网页上增加一行js包的引用就可以了。

~ vi view/admin/moive.html
<html>
<head>
<link href="/css/bootstrap.min.css" rel="stylesheet" media="screen" />
<script type="text/javascript" src="/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/js/bootstrap-paginator.min.js"></script>
</head>
<body>
<!-- 代码片断 查询表单 -->
<form method="GET">
...
</form>

<!-- 代码片段 分页控件 -->
<div class="span12">
<div id="page1"></div>
</div>

<!-- 代码片段 查询结果 -->
<div class="span12">
<table class="table table-hover table-striped">
...
</table>
</div>

<!-- 代码片段 -->
<script type="text/javascript">
$(function(){

//分页功能
var options = {
currentPage:2,
totalPages:5,
numberOfPages:5
}
$('#page1').bootstrapPaginator(options);

})
</script>

</body>
</html>

显示效果:

p2

2. 分页后端查询mongoose

使用mongoose访问mongodb,在 Mongoose使用案例–让JSON数据直接入库MongoDB 一文中已经讲过。

我们构建分布查询的DAO。

  • 这里Movie是model类型,没有用到entity。直接通过model做的分页查询。
  • 分页查询是二步查询,第一步查询结果集数据,第二步查询总记录
  • 通过skip,limit进行分页。(关于skip的效率问题,10W以下的记录,skip性能是可以接受的,10w以上的记录需要根据排序的字段,通过索引定位查询,这个概念以后再讲。)
  • 排序功能默认用插入时间倒叙:sort(‘-create_date’)

查询参数:

  • q,查询条件
  • col,数据返回字段
  • pageNumber,当前是第几页,如果不存在默认为第1页
  • resultsPerPage,每页多少条记录

分页的返回值

  • null:空错误,因为错误已经通过if处理了
  • pageCount:一共有多少页
  • results:数据结果集

~ vi model/Movie.js

var Movie = mongodb.mongoose.model("Movie", MovieSchema);
var MovieDAO = function(){};

//代码片段

MovieDAO.prototype.findPagination = function(obj,callback) {
  var q=obj.search||{}
  var col=obj.columns;

  var pageNumber=obj.page.num||1;
  var resultsPerPage=obj.page.limit||10;

  var skipFrom = (pageNumber * resultsPerPage) - resultsPerPage;
  var query = Movie.find(q,col).sort('-create_date').skip(skipFrom).limit(resultsPerPage);

  query.exec(function(error, results) {
    if (error) {
      callback(error, null, null);
    } else {
      Movie.count(q, function(error, count) {
        if (error) {
          callback(error, null, null);
        } else {
          var pageCount = Math.ceil(count / resultsPerPage);
          callback(null, pageCount, results);
        }
      });
    }
  });
}

 

3. 分页结果返回页面(后端->前端)

传参数到页面:page对象

  • limit:5,每页限制5条记录
  • num:1,查询的页面
  • pageCount,一共有多少页
  • size,当前页面有多少条记录
  • numberOf,分页用几个标签显示

~ vi routes/moive.js

//代码片段
exports.movie = function(req, res) {
var search={};
var page={limit:5,num:1};

//查看哪页
if(req.query.p){
page['num']=req.query.p<1?1:req.query.p;
}

var model = {
search:search,
columns:'name alias director publish images.coverSmall create_date type deploy',
page:page
};

Movie.findPagination(model,function(err, pageCount, list){
page['pageCount']=pageCount;
page['size']=list.length;
page['numberOf']=pageCount>5?5:pageCount;

return res.render('admin/movie', {
title:'电影|管理|moive.me',
page:'admin',nav:'admin.movie',
movieList:list,
page:page
});
});
}

4. 分页查询参数传递(前端->后端)

前端向后端:参数传递过程

  • 通过javascript,从页面向控制器传参数p.
  • 通过composeUrlParams函数,封装原表单的查询参数
  • 通过pageUrl函数,拼接带分布的url请求

后端向前端:参数传递过程

  • 如果page1的div增加自定义属性
  • 在页面渲染时,通过js解析div增加自定义属性,赋值给bootstrap-paginator控件
~ vi view/admin/moive.html

//修改分页的div,增加自定义的属性
<div class="span12">
<div id="page1" pageCount="<%=page.pageCount%>" pageNum="<%=page.num %>" pageSize="<%=page.size%>" pageLimit="<%=page.limit%>" numberOfPages="<%=page.numberOf%>"></div>

<!-- 代码片段 -->
<script type="text/javascript">
$(function(){

//获得浏览器参数
$.extend({
getUrlVars: function(){
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++){
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
},
getUrlVar: function(name){
return $.getUrlVars()[name];
}
});

//封装浏览器参数
var composeUrlParams=function(){
var param='';
$.each($.getUrlVars(), function(i, item) {
if(item!='p'){
var val=$.getUrlVar(item);
if(val) param += "&" + item+"="+val;
}
});
return param;
}

//分页功能
var page=$('#page1');
var options = {
currentPage:page.attr('pageNum'),
totalPages:page.attr('pageCount'),
numberOfPages:page.attr('numberOfPages'),
pageUrl: function(type, page, current){
return "/admin/movie?"+composeUrlParams()+"&p="+page;
}
}
$('#page1').bootstrapPaginator(options);
})
</script>

5. 分页完整展现

p1

 

分页功能可能实现起来稍有复杂,如果可以完整地封装成一个控件,就会变得非常简单了。不知道是否已经有人做好了这个控件。

如果有时间,我可能也会写一下。希望能帮助到对分页还陌生的朋友。

 

转载请注明出处:
http://blog.fens.me/nodejs-bootstrap-paginator/

打赏作者

This entry was posted in Javascript语言实践

  • she1990111

    如果是只有一页呢

    • 一页,你也可以控制怎么显示。也可以隐藏分页的标签。

      • she1990111

        我不太懂怎么控制,我控制出来以后就只有一个[1],但是我想显示<< >>这个样子,你可以帮我吗?谢谢!给我说下思路

        • 你说的这个样子,如果不符合控件的定义,需要修改代码来处理。当你自己有能力的再改,不然就用默认的样式。

  • eric

    var options = {
    currentPage:page.attr(‘pageNum’),
    totalPages:page.attr(‘pageCount’),
    numberOfPages:page.attr(‘numberOfPages’)
    }
    这里的属性能通过session赋值吗? currentPage : req.session.page.currentPage,

    • 不能通过session赋值。
      1. req.session的作用域是express渲染的ejs模板
      2. options是页面上面的js

  • CccccYj

    前几篇不想下载程序代码的都有附上程序代码下载,这篇想下载程序代码看看,竟找不到下载的…

  • liuwu

    这个项目能否上传一下

  • liuwu

    不知道怎么回事,导进相应js文件后生成的效果不一样。不知道是不是官网的插件bootstrap-paginator.min.js版本问题。希望能看一下你这个项目的源代码。

    • blueandhack

      已查阅文档 需要在option加入bootstrapMajorVersion:3
      表示使用的bootstrap为3+就可以正常显示了主要是2和3的区别在于
      前者将pagination的样式加到div,而后者加入到ul了

      • 谢谢提醒,稍后我升级一下文章中的软件版本。

  • liuwu

    我导进来效果是这样的

    • 只是显示有问题吧?我之前是基于bootstrap2做的,你动手调一下css呢?

    • zhu91aizhu

      你的bootstrap如果是3.x的跟元素应该是。
      而且变量options应该增加一个字段bootstrapMajorVersion:3

  • feiyang

    请教,谢谢,如果查询结果记录数是0,会出现Uncaught Page out of range 错误,不知道有没有好办法解决,我正在想办法….

    • 在后台里,提前判断一个 结果集数,传到前台的数据不要包含错误的数据。