You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mongoose分页可视化实现问题(Mustache+Bootstrap3)

搞定mongoose-paginate分页+Bootstrap 3可视化按钮的方案

看起来你已经走对了第一步——集成了mongoose分页插件,但路由逻辑没跟上,而且还缺前端的分页渲染部分。我帮你一步步梳理解决:

先把后端路由的坑填上

你现在的代码里同时调用了User.paginate()和User.find(),而且paginate回调里的减法操作完全没发挥作用,等于白做了分页查询。我们要把分页结果直接传给模板,而不是查所有用户。

修改后的路由代码应该是这样:

router.get('/dashboard', ensureAuthenticated, (req, res) => {
  // 从URL参数拿当前页码,默认显示第1页
  const currentPage = parseInt(req.query.page) || 1;
  const pageSize = 6;

  User.paginate({}, { page: currentPage, limit: pageSize }, (err, paginatedResult) => {
    if (err) {
      console.error('分页查询出错:', err);
      return res.status(500).send('服务器内部错误');
    }

    // 把分页核心数据和用户列表一起传给模板
    res.render('dashboard/index.hbs', {
      pageTitle: 'Dashboard',
      users: paginatedResult.docs, // 这是当前页的用户列表
      pagination: {
        current: paginatedResult.page,
        totalPages: paginatedResult.totalPages,
        hasPrev: paginatedResult.hasPrevPage,
        hasNext: paginatedResult.hasNextPage,
        prevPage: paginatedResult.prevPage,
        nextPage: paginatedResult.nextPage
      }
    });
  });
});

这里做了几个关键优化:

  • 支持从URL的?page=X参数切换页码,用户点击分页按钮时就能传递这个参数
  • 把分页的关键状态(当前页、总页数、是否有上下页等)打包成pagination对象,方便前端渲染按钮

用Bootstrap 3做可点击的分页组件

接下来在你的Handlebars(兼容Mustache语法)模板dashboard/index.hbs里,用户列表下方添加分页按钮。我们利用后端传过来的pagination数据动态生成:

方案1:适合Handlebars(带辅助函数)

如果你的模板引擎是Handlebars,可以注册eq、add这些辅助函数,然后这样写:

<!-- 用户列表下方的分页组件 -->
<nav aria-label="用户列表分页">
  <ul class="pagination">
    <!-- 上一页按钮 -->
    <li class="{{#unless pagination.hasPrev}}disabled{{/unless}}">
      <a href="/dashboard?page={{pagination.prevPage}}" aria-label="上一页">
        <span aria-hidden="true">&laquo;</span>
      </a>
    </li>

    <!-- 生成所有页码按钮 -->
    {{#repeat pagination.totalPages}}
      <li class="{{#if (eq @index pagination.current)}}active{{/if}}">
        <a href="/dashboard?page={{add @index 1}}">{{add @index 1}}</a>
      </li>
    {{/repeat}}

    <!-- 下一页按钮 -->
    <li class="{{#unless pagination.hasNext}}disabled{{/unless}}">
      <a href="/dashboard?page={{pagination.nextPage}}" aria-label="下一页">
        <span aria-hidden="true">&raquo;</span>
      </a>
    </li>
  </ul>
</nav>

注:如果没注册辅助函数,你需要先在Handlebars里注册,比如:

const hbs = require('hbs');
// 注册相等判断辅助函数
hbs.registerHelper('eq', function(a, b) {
  return a === b;
});
// 注册加法辅助函数
hbs.registerHelper('add', function(a, b) {
  return a + b;
});
// 注册循环辅助函数
hbs.registerHelper('repeat', function(n, options) {
  let output = '';
  for(let i = 0; i < n; i++) {
    output += options.fn({index: i});
  }
  return output;
});

方案2:纯Mustache兼容写法(无自定义辅助函数)

如果是纯Mustache,逻辑处理要放在后端。先在路由里生成带激活状态的页码数组:

// 在paginate回调里添加
const pageList = [];
for (let i = 1; i <= paginatedResult.totalPages; i++) {
  pageList.push({
    number: i,
    isActive: i === paginatedResult.page
  });
}

// 把pageList加到渲染数据里
res.render('dashboard/index.hbs', {
  // ...其他数据
  pageList: pageList
});

然后模板里这样写:

<nav aria-label="用户列表分页">
  <ul class="pagination">
    <li class="{{#unless pagination.hasPrev}}disabled{{/unless}}">
      <a href="/dashboard?page={{pagination.prevPage}}" aria-label="上一页">
        <span aria-hidden="true">&laquo;</span>
      </a>
    </li>

    {{#each pageList}}
      <li class="{{#if isActive}}active{{/if}}">
        <a href="/dashboard?page={{number}}">{{number}}</a>
      </li>
    {{/each}}

    <li class="{{#unless pagination.hasNext}}disabled{{/unless}}">
      <a href="/dashboard?page={{pagination.nextPage}}" aria-label="下一页">
        <span aria-hidden="true">&raquo;</span>
      </a>
    </li>
  </ul>
</nav>

最后验证一下

启动服务器后,访问/dashboard,你会看到每页显示6个用户,底部有Bootstrap样式的分页按钮:

  • 点击不同页码会切换到对应页面
  • 第一页时上一页按钮会禁用,最后一页时下一页按钮会禁用
  • 当前页的按钮会高亮显示

另外提个小建议:如果用的是旧版mongoose-paginate,建议换成mongoose-paginate-v2——它是维护中的新版本,返回的结果字段更规范,和我们上面用的paginatedResult.page、totalPages这些字段完全匹配,避免踩版本坑。

内容的提问来源于stack exchange,提问作者user8331511

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:39:18