EditThis final subarticle shows how to define a page to update Book objects. Form handling when updating a book is much like that for creating a book, except that you must populate the form in the GET route with values from the database.

Controller—get route

Open /controllers/bookController.js. Find the exported book_update_get() controller method and replace it with the following code.

The controller gets the id of the Book to be updated from the URL parameter (req.params.id). It uses the async.parallel() method to get the specified Book record (populating its genre and author fields) and lists of all the Author and Genre objects. When all operations have completed it marks the currently selected genres as checked and then renders the book_form.pug view, passing variables for title, book, all authors, and all genres.

Controller—post route

Find the exported book_update_post() controller method, and replace it with the following code.

// Handle book update on POST.
exports.book_update_post =[// Convert the genre to an array(req, res, next)=>{if(!(req.body.genre instanceofArray)){if(typeof req.body.genre==='undefined')
req.body.genre=[];else
req.body.genre=newArray(req.body.genre);}next();},// Validate fields.body('title','Title must not be empty.').isLength({ min:1}).trim(),body('author','Author must not be empty.').isLength({ min:1}).trim(),body('summary','Summary must not be empty.').isLength({ min:1}).trim(),body('isbn','ISBN must not be empty').isLength({ min:1}).trim(),// Sanitize fields.sanitizeBody('title').trim().escape(),sanitizeBody('author').trim().escape(),sanitizeBody('summary').trim().escape(),sanitizeBody('isbn').trim().escape(),sanitizeBody('genre.*').trim().escape(),// Process request after validation and sanitization.(req, res, next)=>{// Extract the validation errors from a request.const errors =validationResult(req);// Create a Book object with escaped/trimmed data and old id.var book =newBook({ title: req.body.title,
author: req.body.author,
summary: req.body.summary,
isbn: req.body.isbn,
genre:(typeof req.body.genre==='undefined')?[]: req.body.genre,
_id:req.params.id //This is required, or a new ID will be assigned!});if(!errors.isEmpty()){// There are errors. Render form again with sanitized values/error messages.// Get all authors and genres for form.async.parallel({
authors:function(callback){
Author.find(callback);},
genres:function(callback){
Genre.find(callback);},},function(err, results){if(err){returnnext(err);}// Mark our selected genres as checked.for(let i =0; i < results.genres.length; i++){if(book.genre.indexOf(results.genres[i]._id)>-1){
results.genres[i].checked='true';}}
res.render('book_form',{ title:'Update Book',authors:results.authors, genres:results.genres, book: book, errors: errors.array()});});return;}else{// Data from form is valid. Update the record.
Book.findByIdAndUpdate(req.params.id, book,{},function(err,thebook){if(err){returnnext(err);}// Successful - redirect to book detail page.
res.redirect(thebook.url);});}}];

This is very similar to the post route used when creating a Book. First we validate and sanitize the book data from the form and use it to create a new Book object (setting its _id value to the id of the object to update). If there are errors when we validate the data then we re-render the form, additionally displaying the data entered by the user, the errors, and lists of genres and authors. If there are no errors then we call Book.findByIdAndUpdate() to update the Book document, and then redirect to its detail page.

View

Open /views/book_form.pug and update the section where the author form control is set to have the conditional code shown below.