Question

I'm having some trouble saving my menu order in NodeJS with Mongoose.

First off I loop out my menu structure via Jade:

form(method='post', action='/admin/pages/order')
    ul
        each page in pages
            li
                input(type='number', name='order', value= page.order)
                |  - 
                a(href='/admin/pages/edit/' + page._id)= page.title
                |  - 
                a(href='/admin/pages/delete/' + page._id) Delete page

    button Save order

page.order is describing the page's current order. Ie. Frontpage 10, About 20, Contact 30 etc. And if I want another order, I should be able to move Frontpage to 25 which will place it between About and Contact, but whenever I save. The order is messed up.

Maybe I'm missing some code below or doing something wrong?

And here is my save post:

var Page = require('../models/page');

app.post('/admin/pages/order', function(req, res) {
    var pageOrder = req.body.order;
    var counter = [];

    Page.find({}, {}, { sort: { 'order' : 1 } }, function(err, pages) {
        pages.forEach(function(page, index) {
            counter.push(true);

            page.order = 0;
            page.save();

            if (counter.length === pages.length) {
                counter = [];
                var count = 1;

                Page.find({}, {}, { sort: { 'order' : 1 } }, function(err, pages) {
                    pages.forEach(function(page, index) {
                        counter.push(true);

                        page.order = 10 * count++;
                        page.save();

                        if (counter.length === pages.length) {
                            res.redirect('/admin/pages');
                        }
                    });
                });
            }
        });
    });
});

Just say if you need more information.

Was it helpful?

Solution

You asked about using async and doing the first sort in memory rather than going to the database twice. You can try this (I haven't tested it). It uses Underscore to do the sorting.

_ = require("underscore");
async = require("async");

// ...

var newPageOrders = req.body.order;

// Get the pages in their original order
Page.find({}, {}, { sort: { 'order' : 1 } }, function(err, pages) {
  if (err) return res.send(err);

  // Set the order on the pages to the new order
  pages.forEach(function (page, index) {
    page.order = newPageOrders[index];
  });

  // Use the Underscore library to sort the list of pages by its new ordering
  pages = _.sortBy(pages, 'order');

  // Now that we've sorted the pages, normalize the order numbers
  pages.forEach(function (page, index) {
    page.order = index*10
  });

  // Save all of the pages
  async.forEach(pages, function (page, callback) {
    // Save the page. Pass in the callback which async provides us with,
    // so that async knows when the page is saved
    page.save(callback);
  }, function (err) {
    // This function is only called once all of the pages are saved
    if (err) res.send(err);
    else res.redirect('/admin/pages');
  });
});

OTHER TIPS

It is too hard to understand what you want, below is a brief description of what do I understand.

//You have following `pages` records : 
{
title : 'Frontpage'
order:10
}
,
{
title : 'About'
order:20
}
,
{
title : 'Contact'
order:30
}

And now you want to change/modify/update the order of some page (ie. Frontpage to 25).

If I understand you correctly then I would advise you to use a different route for handling those updates.

/*
What the following handler does is :
Whenever you attempt to change the page order, first of all it tries to get one with the same order and assign its order to the page's order that is going to be updated. (I hope it is clear enough)
*/
app.put('/admin/pages/:pageId/order', function(req, res) {

Page.findOne({order:req.body.order}, function(err, doc){

       Page.findById(req.params.pageId, function(err, page) {
          var pageOrder = page.order;
          page.order = req.body.order;
          page.save();

          if(doc) {
           doc.order = pageOrder;
           doc.save();
          }
       });
});
});

I solved the problem. By doing this instead:

app.post('/admin/pages/order', function(req, res) {
    var pageOrder = req.body.order;
    var counter = [];
    var count = 1;

    Page.find({}, {}, { sort: { 'order' : 1 } }, function(err, pages) {
        pages.forEach(function(page, index) {
            counter.push(true);

            Page.findOneAndUpdate({order: page.order}, {order: pageOrder[index]}, true, function(err) {
                if (err)
                    res.send(err);
            });

            if (counter.length === pages.length) {
                counter = [];

                Page.find({}, {}, { sort: { 'order' : 1 } }, function(err, pages) {
                    pages.forEach(function(page, index) {
                        counter.push(true);

                        page.order = count++ * 10;
                        page.save();

                        if (counter.length === pages.length) {
                            res.redirect('/admin/pages');
                        }
                    });
                });
            }
        });
    });
});

I had to use the findOneAndUpdate in order to get a fresh list of my menu. So I could clean up the order with count++ * 10 and then save it to the database.

Is this the smartest way of doing this? And page.save() doesnt update the database right away?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top