3

I am working on a blogging application (click the link to see the GitHub repo) with Express, EJS and MongoDB.

I have made a simple pager for the posts.

In the posts controller I have:

exports.getPosts = async (req, res, next) => {

  const posts = await Post.find({}, (err, posts) => {

      const perPage = 10;

      const currPage = req.query.page ? parseInt(req.query.page) : 1;

      const postsCount = posts.length;

      const pageCount = Math.ceil(postsCount / perPage);

      const pageDecrement = currPage > 1 ? 1 : 0;

      const pageIncrement = currPage < pageCount ? 1 : 0;

      if (err) {
        console.log("Error: ", err);
      } else {
        res.render("default/index", {
          moment: moment,
          layout: "default/layout",
          website_name: "MEAN Blog",
          page_heading: "XPress News",
          page_subheading: "A MEAN Stack Blogging Application",
          currPage: currPage,
          pageDecrement: pageDecrement,
          pageIncrement: pageIncrement,
          posts: posts,
        });
      }
    })
      .sort({ created_at: -1 })
      .populate("category")
      .limit(perPage)
      .skip((currPage - 1) * perPage);
};

The pager in the view:

<% if (posts) {%>
  <div class="clearfix d-flex justify-content-center">
    <div class="px-1">
        <a class="btn btn-primary <%= pageDecrement == 0 ? 'disabled' : '' %>" href="/?page=<%= currPage - pageDecrement %>">&larr; Newer Posts</a>
    </div>

    <div class="px-1">
        <a class="btn btn-primary <%= pageIncrement == 0 ? 'disabled' : '' %>" href="/?page=<%= currPage + pageIncrement %>">Older Posts &rarr;</a>
    </div>
  </div>
<% } %>

The problem

The line .limit(perPage) from the controller gives the error perPage is not defined in the console (Git bash).

The solution that does not work

Clearly, I can move these 2 lines above const posts

const perPage = 5;
const currPage = req.query.page ? parseInt(req.query.page) : 1;

but I can not do the same with const postsCount = posts.length; (which I also need in the view).

The objective

I am trying to make the snippet of code regarding the pagination reusable (like a plugin, if possible), since I need to paginate for the posts filtered by category, and also the list of post in the admin section o the application.

What am I doing wrong?

2
  • I didn't run the code, but I think the perPage variable is the correct scope. Try to define it before const posts. Commented May 10, 2020 at 21:01
  • Check these answers, they can help you Pagination Mongoose Commented May 13, 2020 at 0:44

5 Answers 5

5
+50

why are you using callback and await together.seems like you need to look in to async / await and promises.What you can do is as below:

exports.getPosts = async (req, res, next) => {
  const currPage = req.query.page ? parseInt(req.query.page) : 1;
  const perPage = 10;
  try {
    const posts = await Post.find({})
      .sort({ created_at: -1 })
      .populate("category")
      .limit(perPage)
      .skip((currPage - 1) * perPage).exec();

    const postsCount = posts.length;

    const pageCount = Math.ceil(postsCount / perPage);

    const pageDecrement = currPage > 1 ? 1 : 0;

    const pageIncrement = currPage < pageCount ? 1 : 0;
    res.render("default/index", {
      moment: moment,
      layout: "default/layout",
      website_name: "MEAN Blog",
      page_heading: "XPress News",
      page_subheading: "A MEAN Stack Blogging Application",
      currPage: currPage,
      pageDecrement: pageDecrement,
      pageIncrement: pageIncrement,
      posts: posts,
    });
  } catch (err) {
    console.log("Error: ", err);
    // add proper error handling here 
    res.render('default/error', {
      err
    });
  }
};
Sign up to request clarification or add additional context in comments.

5 Comments

I think your Post.find() is missing the err and posts arguments.
If you use async await, no need to pass callback
UnhandledPromiseRejectionWarning: ReferenceError: err is not defined at exports.getPosts (C:\Users\Asus\Desktop\XPressBlog\controllers\front-end\posts.js:23:3) Line 23: if (err) {.
try updated code, it uses try catch for error handling, also look in to async/await and error handling
1

As a comment correctly points out, this seems to be an issue of scoping.

exports.getPosts = async (req, res, next) => {
  // getPosts local scope
  const posts = await Post.find({}, (err, posts) => {
      // ... local scope
      const perPage = 10;

      const currPage = req.query.page ? parseInt(req.query.page) : 1;

      const postsCount = posts.length;

      const pageCount = Math.ceil(postsCount / perPage);

      const pageDecrement = currPage > 1 ? 1 : 0;

      const pageIncrement = currPage < pageCount ? 1 : 0;

      if (err) {
        console.log("Error: ", err);
      } else {
        res.render("default/index", {
          moment: moment,
          layout: "default/layout",
          website_name: "MEAN Blog",
          page_heading: "XPress News",
          page_subheading: "A MEAN Stack Blogging Application",
          currPage: currPage,
          pageDecrement: pageDecrement,
          pageIncrement: pageIncrement,
          posts: posts,
        });
      }
      // ... local scope ended
    })
      .sort({ created_at: -1 })
      .populate("category")
      // perPage == undefined
      .limit(perPage)
      .skip(currPage - 1)) * perPage;
};

Move const perPage to above the const posts and it'll be within the correct scope/context.

exports.getPosts = async (req, res, next) => {
  const perPage = 10;
  // getPosts local scope
  const posts = await Post.find({}, (err, posts) => {
      // ... rest of func
    });
  // .. rest of func
};

2 Comments

This solution solves one problem, but leaves/creates others: I have a postsCount variable that uses the posts variable.
You can reference the outer scope using the local scope, but not the other way around. This article can explain this far better than I can: scotch.io/tutorials/understanding-scope-in-javascript
1

Maybe if you check this component, you can solve your problem by seeing how it works

Mongoose Paginate

$ npm install mongoose-paginate

/**
* querying for `all` {} items in `MyModel`
* paginating by second page, 10 items per page (10 results, page 2)
**/

MyModel.paginate({}, 2, 10, function(error, pageCount, paginatedResults) {
 if (error) {
   console.error(error);
 } else {
   console.log('Pages:', pageCount);
   console.log(paginatedResults);
 }
}

Comments

1

As explained by other comments. This could just be a problem with scope.

exports.getPosts = async (req, res, next) => {
  const perPage = 10; // enable you to use perPage inside getPosts()
  let postsCount = 0; // let, to enable you to use/reassigned postsCount inside getPosts()
  const posts = await Post.find({}, (err, posts) => {
    // ...
    postsCount = posts.length; // updates the variable with the correct count
    // ...
    if (err) {
        console.log("Error: ", err);
      } else {
        res.render("default/index", {
          //...
          posts: posts,
          postsCount: posts.length, // for explicit postsCount in the view

        });
      }

  });

  // ..


for you problem with postsCount in the view, since you already have posts in you view

<% if (posts) {%>
  ...
<% } %>

I think you can just use posts.length or postsCounts in the view like how you use the others(e.g pageIncrement, currPage, pageIncrement);

4 Comments

I already did that, with var postsCount = 0;, because I can't change the value of a constant.
Hello @harold-pogi and welcome to SO! Next time please make sure to not just post code but instead explain why this solution might work and what is wrong. It helps a lot!
While this code may resolve the OP's issue, it is best to include an explanation as to how your code addresses the OP's issue. In this way, future visitors can learn from your post, and apply it to their own code. SO is not a coding service, but a resource for knowledge. Also, high quality, complete answers are more likely to be upvoted. These features, along with the requirement that all posts are self-contained, are some of the strengths of SO as a platform, that differentiates it from forums. You can edit to add additional info &/or to supplement your explanations with source documentation.
Thanks. I've update my answers, also included explanation and added postsCount
0

As I found out recently postsCount = posts.length counts the posts after they are limited by .skip((currPage - 1) * perPage), so the pageIncrement variable "equation" turns to:

let pageIncrement = postsCount >= perPage ? 1 : 0;

So I get, in the controller:

exports.getPosts = async (req, res, next) => {

    const perPage = 5;

    const currPage = req.query.page ? parseInt(req.query.page) : 1;

    let postsCount = 0;

    const posts = await Post.find({}, (err, posts) => {

            postsCount = posts.length;

            let pageDecrement = currPage > 1 ? 1 : 0;

            let pageIncrement = postsCount >= perPage ? 1 : 0;

            if (err) {
                console.log('Error: ', err);
            } else {
                res.render('default/index', {
                    moment: moment,
                    layout: 'default/layout',
                    website_name: 'MEAN Blog',
                    page_heading: 'XPress News',
                    page_subheading: 'A MEAN Stack Blogging Application',
                    currPage: currPage,
                    posts: posts,
                    pageDecrement: pageDecrement,
                    pageIncrement: pageIncrement
                });
            }
        })
        .sort({
            created_at: -1
        })
        .populate('category')
        .limit(perPage)
        .skip((currPage - 1) * perPage);
};

And in the view:

<a class="btn btn-primary <%= pageDecrement == 0 ? 'disabled' : '' %>" href="/?page=<%= currPage - pageDecrement %>">&larr; Newer Posts</a>

and

<a class="btn btn-primary <%= pageIncrement == 0 ? 'disabled' : '' %>" href="/?page=<%= currPage + pageIncrement %>">Older Posts &rarr;</a>

That works fine unless there are is a number of posts equal to perPage x N, where N is an integer, in which case the "Older Posts" button becomes disabled one page too late.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.