You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Node.js中Mongoose与Handlebars的分页实现求助

Hey there! Let's break this down step by step—first handling the pagination logic with Mongoose, then hooking it up to your Handlebars frontend. I've worked through this exact setup plenty of times, so I'll make it straightforward for you.

1. Mongoose Backend Pagination

You've got two solid options here: using Mongoose's built-in methods, or leveraging a pagination plugin for less boilerplate.

Option 1: Native Mongoose Methods (No Plugins)

This is great if you want full control over the logic.

First, define constants for how many posts you want per page, then grab the current page from the request query parameters:

// In your route handler (e.g., /posts)
const ITEMS_PER_PAGE = 10; // Adjust this to your preference
const page = parseInt(req.query.page) || 1; // Default to page 1 if no page is specified
const skip = (page - 1) * ITEMS_PER_PAGE; // Calculate how many posts to skip

// Fetch the paginated posts (sorted by newest first here)
const posts = await Post.find()
  .sort({ createdAt: -1 })
  .skip(skip)
  .limit(ITEMS_PER_PAGE);

// Get total number of posts to calculate total pages
const totalPosts = await Post.countDocuments();
const totalPages = Math.ceil(totalPosts / ITEMS_PER_PAGE);

// Pass data to your Handlebars template
res.render('posts', {
  posts,
  currentPage: page,
  totalPages
});

Option 2: Use the mongoose-paginate-v2 Plugin

This plugin simplifies the process by wrapping the pagination logic into a single method.

  1. Install it first:
npm install mongoose-paginate-v2
  1. Add it to your Post model:
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');

const postSchema = new mongoose.Schema({
  title: String,
  content: String,
  // ... other fields
});

postSchema.plugin(mongoosePaginate);

module.exports = mongoose.model('Post', postSchema);
  1. Use it in your route handler:
const ITEMS_PER_PAGE = 10;
const page = parseInt(req.query.page) || 1;

const result = await Post.paginate({}, { 
  page: page, 
  limit: ITEMS_PER_PAGE,
  sort: { createdAt: -1 }
});

// The result object includes:
// - docs: your paginated posts
// - totalPages: total number of pages
// - page: current page number

res.render('posts', {
  posts: result.docs,
  currentPage: result.page,
  totalPages: result.totalPages
});
2. Handlebars Frontend Implementation

Now that your backend is sending the right data, let's render the posts and pagination controls.

First, render your list of posts:

{{!-- posts.handlebars template --}}
<div class="posts-container">
  {{#each posts}}
    <div class="post-card">
      <h2>{{title}}</h2>
      <p>{{content}}</p>
      <a href="/posts/{{_id}}">Read More</a>
    </div>
  {{else}}
    <p>No posts found!</p>
  {{/each}}
</div>

Next, add the pagination controls. Handlebars doesn't have built-in helpers for math or comparisons, so we'll register some custom helpers first in your Express setup:

const express = require('express');
const { engine } = require('express-handlebars');

const app = express();

// Register custom Handlebars helpers
app.engine('handlebars', engine({
  helpers: {
    // Math helpers
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    // Comparison helpers
    gt: (a, b) => a > b,
    lt: (a, b) => a < b,
    // Generate a range of page numbers to display (avoids showing 100+ page buttons)
    getPageRange: (currentPage, totalPages) => {
      const range = [];
      const start = Math.max(1, currentPage - 3); // Show up to 3 pages before current
      const end = Math.min(totalPages, currentPage + 3); // Show up to 3 pages after current
      for (let i = start; i <= end; i++) {
        range.push(i);
      }
      return range;
    }
  }
}));
app.set('view engine', 'handlebars');

Now add the pagination HTML to your template:

<div class="pagination">
  {{!-- Previous Page Button --}}
  {{#if (gt currentPage 1)}}
    <a href="/posts?page={{subtract currentPage 1}}" class="pagination-btn">← Previous</a>
  {{/if}}

  {{!-- Page Number Buttons --}}
  {{#each getPageRange currentPage totalPages}}
    {{#if (eq this currentPage)}}
      <span class="pagination-btn active">{{this}}</span>
    {{else}}
      <a href="/posts?page={{this}}" class="pagination-btn">{{this}}</a>
    {{/if}}
  {{/each}}

  {{!-- Next Page Button --}}
  {{#if (lt currentPage totalPages)}}
    <a href="/posts?page={{add currentPage 1}}" class="pagination-btn">Next →</a>
  {{/if}}
</div>

A quick edge case tip: if totalPages is 1, you might want to hide the pagination controls entirely. Add this check to wrap the pagination code:

{{#if (gt totalPages 1)}}
  {{!-- Render pagination controls here --}}
{{/if}}

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

火山引擎 最新活动