looking for some solutions? You are welcome.

SOLVED: Can I send an array of objects to the view, in the controller, associated with a mongodb object in node/ express/ mongoose?

Cheesus Toast:

I have been following a tutorial on the MDN site for Node/ express/ mongoose. It may be familiar to many people but I will put the code down anyway. What I want to do is create a view that is similar to the book_list page, however, I wish to have the ability to send the book instances with each book (details will follow). In other words I wish to be able to have the BookInstances for each book as part of the book object on the list page - it is mainly for the count (or length) but I may wish to also use it in other ways.

The book model

var mongoose = require('mongoose');

var Schema = mongoose.Schema;

var BookSchema = new Schema({
title: {type: String, required: true},
author: { type: Schema.ObjectId, ref: 'Author', required: true },
summary: {type: String, required: true},
isbn: {type: String, required: true},
genre: [{ type: Schema.ObjectId, ref: 'Genre' }]
});

// Virtual for this book instance URL.
BookSchema
.virtual('url')
.get(function () {
  return '/catalog/book/'+this._id;
});

// Export model.
module.exports = mongoose.model('Book', BookSchema);

BookInstance Schema part of the Model:

var BookInstanceSchema = new Schema(
{
  book: { type: Schema.Types.ObjectId, ref: 'Book', required: true },//reference to the associated book
  imprint: { type: String, required: true },
  status: { type: String, required: true, enum: ['Available', 'Maintenance', 'Loaned', 'Reserved'], default: 'Maintenance' },
  due_back: { type: Date, default: Date.now }
 }
);

The book_list controller:

// Display list of all Books.
exports.book_list = function(req, res, next) {

Book.find({}, 'title author')
  .populate('author')
  .exec(function (err, list_books) {
    if (err) { return next(err); }
    //Successful, so render
    res.render('book_list', { title: 'Book List', book_list: list_books });
  });

};

The book detail controller:

// Display detail page for a specific book.
exports.book_detail = function(req, res, next) {

async.parallel({
    book: function(callback) {

        Book.findById(req.params.id)
          .populate('author')
          .populate('genre')
          .exec(callback);
    },
    book_instance: function(callback) {

      BookInstance.find({ 'book': req.params.id })
      .exec(callback);
    },
}, function(err, results) {
    if (err) { return next(err); }
    if (results.book==null) { // No results.
        var err = new Error('Book not found');
        err.status = 404;
        return next(err);
    }
    // Successful, so render.
    res.render('book_detail', { title: 'Book Detail', book: results.book, 
book_instances: results.book_instance } );
  });

   };

I have a feeling it must be something that can maybe be done with populate but I have not got that to work. The only way I have managed to get the book instance object to appear in the list for each book item is to send all book instances to the view. From there I use a foreach loop and then IF statement to get the book instances for each book. It looks really ugly and I am sure there must be some other way to do this. I am used to asp.net mvc - in that you use a virtual object. I am not sure if I am supposed to modify the model here or the controller. I may also want to pass in a much more complex model with lists within lists.

I have noted the genre is actually saved into the book document unlike bookinstances - hence the lines in the book detail controller:

  book_instance: function(callback) {
  BookInstance.find({ 'book': req.params.id })
  .exec(callback);
},

Below I have shown what I have done. I could also have done this as objects in the controller but this is what I have now:

Book Controller:

exports.book_list = function (req, res, next) {

async.parallel({
    books: function (callback) {
        Book.find()
            .exec(callback)
    },
    bookinstances: function (callback) {
        BookInstance.find()
            .exec(callback)
    },
}, function (err, results) {
    if (err) { return next(err); } // Error in API usage.
    // Successful, so render.
    res.render('book_list', { title: 'Book Detail', books: results.books, 
bookinstances: results.bookinstances });
});
};

book_list.pug code:

extends layout

block content
 h1= title

table.table
th Book
th BookInstance Count
th 
//- above th is for buttons only (no title)
each book in books
  - var instCount = 0
  each bookinstance in bookinstances
    if book._id.toString() === bookinstance.book.toString()
      - instCount++
  tr 
    td 
      a(href=book.url) #{book.title}
    td #{instCount}

    td
      a.btn.btn-sm.btn-primary(href=book.url+'/update') Update
      if !instCount
        a.btn.btn-sm.btn-danger(href=book.url+'/delete') Delete

else
  li There are no books.

What the page comes out as:

enter image description here



Posted in S.E.F
via StackOverflow & StackExchange Atomic Web Robots
Share:

No comments:

Recent