Home > database >  MVC - Adding Update Functionality to the Model - Node.js Express & MongoDB
MVC - Adding Update Functionality to the Model - Node.js Express & MongoDB

Time:11-20

I'm developing a Node.js Express & MongoDB web application.

Actually, I'm trying to use the MVC feature. However, there is a problem with my code after that I added the update functionality to the model.

Indeed, when I try to update a post, my application crashes and I get the following error message:

TypeError: C:\Users\DELL\Desktop\node-com4muz-database-mvc\views\admin\posts-list.ejs:19
    17|       <ol id="posts-list">
    18|         <% for (const post of posts) { %>
 >> 19|         <li><%- include('includes/post-item', { post: post }) %></li>
    20|         <% } %>
    21|       </ol>
    22|       <% } %>

C:\Users\DELL\Desktop\node-com4muz-database-mvc\views\admin\includes\post-item.ejs:3
    1| <article >
    2|   <h2><%= post.title %></h2>
 >> 3|   <p >Rédigé par <%= post.author.name %></p>
    4|   <p><%= post.summary %></p>
    5|   <div >
    6|     <form action="/blog/<%= post._id %>/delete" method="POST">

Cannot read properties of undefined (reading 'name')
    at eval ("C:\\Users\\DELL\\Desktop\\node-com4muz-database-mvc\\views\\admin\\includes\\post-item.ejs":15:38)
    at post-item (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\ejs\lib\ejs.js:703:17)
    at include (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\ejs\lib\ejs.js:701:39)
    at eval ("C:\\Users\\DELL\\Desktop\\node-com4muz-database-mvc\\views\\admin\\posts-list.ejs":29:17)
    at posts-list (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\ejs\lib\ejs.js:703:17)
    at tryHandleCache (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\ejs\lib\ejs.js:274:36)
    at View.exports.renderFile [as engine] (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\ejs\lib\ejs.js:491:10)
    at View.render (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\express\lib\view.js:135:8)
    at tryRender (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\express\lib\application.js:657:10)
    at Function.render (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\express\lib\application.js:609:3) {
  path: 'C:\\Users\\DELL\\Desktop\\node-com4muz-database-mvc\\views\\admin\\posts-list.ejs'
}

Actually, before I changed the code in the model file (see below) and the route file (see below), the update functionality was working fine.

That's the reason why I think we should compare both codes, before and after adding the update functionality to the model, to find where is the issue.

Here is my code before adding the update functionality to the model:

routes\admin\blog.js:

const express = require('express');
const mongodb = require('mongodb');
const multer = require('multer');

const storageConfig = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'public/admin/images');
  },
  filename: function (req, file, cb) {
    cb(null, Date.now()   '-'   file.originalname);
  }
});

const db = require('../../data/database');

const Post = require('../../models/post');

const ObjectId = mongodb.ObjectId;

const upload = multer({ storage: storageConfig });

const router = express.Router();

router.get('/posts', async function (req, res) {
  const posts = await db
    .getDb()
    .collection('posts')
    .find({})
    .project({ title: 1, summary: 1, 'author.name': 1 })
    .toArray();
  res.render('posts-list', { posts: posts });
});

router.get('/new-post', async function (req, res) {
  const authors = await db.getDb().collection('authors').find().toArray();
  res.render('create-post', { authors: authors });
});

router.post('/new-post', upload.single('image'), async function (req, res) {
  const uploadedImageFile = req.file;

  const authorId = new ObjectId(req.body.author);
  const author = await db
    .getDb()
    .collection('authors')
    .findOne({ _id: authorId });

  const enteredTitle = req.body.title;
  const enteredSummary = req.body.summary;
  const enteredContent = req.body.content;
  const date = new Date();
  const selectedAuthor = {
    author: {
      id: authorId,
      name: author.name,
      email: author.email
    }
  };
  const selectedImage = uploadedImageFile.path;

  const post = new Post(
    enteredTitle,
    enteredSummary,
    enteredContent,
    date,
    selectedAuthor.author,
    selectedImage
  );
  await post.save();

  res.redirect('/posts');
});

router.get('/blog/:id/edit', async function (req, res) {
  const postId = req.params.id;
  const post = await db
    .getDb()
    .collection('posts')
    .findOne(
      { _id: new ObjectId(postId) },
      { title: 1, summary: 1, content: 1 }
    );

  if (!post) {
    return res.status(404).render('404');
  }

  res.render('update-post', { post: post });
});

router.post('/blog/:id/edit', async function (req, res) {
  const postId = new ObjectId(req.params.id);
  const result = await db
    .getDb()
    .collection('posts')
    .updateOne(
      { _id: postId },
      {
        $set: {
          title: req.body.title,
          summary: req.body.summary,
          content: req.body.content
          // date: new Date()
        }
      }
    );

  res.redirect('/posts');
});

router.post('/blog/:id/delete', async function (req, res) {
  const postId = new ObjectId(req.params.id);
  const result = await db
    .getDb()
    .collection('posts')
    .deleteOne({ _id: postId });
  res.redirect('/posts');
});

router.get('/admin', async function (req, res) {
  if (!res.locals.isAuth) {
    // if (!req.session.user)
    return res.status(401).render('401');
  }

  if (!res.locals.isAdmin) {
    return res.status(403).render('403');
  }

  res.render('admin');
});

module.exports = router;

models\post.js:

const db = require('../data/database');

class Post {
  constructor(title, summary, content, date, author, image, id) {
    this.title = title;
    this.summary = summary;
    this.content = content;
    this.date = date;
    this.author = author;
    this.image = image;
    this.id = id; // may be undefined
  }

  async save() {
    const newPost = {
      title: this.title,
      summary: this.summary,
      content: this.content,
      date: this.date,
      author: this.author,
      imagePath: this.image
    };

    const result = await db.getDb().collection('posts').insertOne(newPost);
    // console.log(result);
    return result;
  }
}

module.exports = Post;

And here is the code after adding the update functionality to the model:

routes\admin\blog.js:

const express = require('express');
const mongodb = require('mongodb');
const multer = require('multer');

const storageConfig = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'public/admin/images');
  },
  filename: function (req, file, cb) {
    cb(null, Date.now()   '-'   file.originalname);
  }
});

const db = require('../../data/database');

const Post = require('../../models/post');

const ObjectId = mongodb.ObjectId;

const upload = multer({ storage: storageConfig });

const router = express.Router();

router.get('/posts', async function (req, res) {
  const posts = await db
    .getDb()
    .collection('posts')
    .find({})
    .project({ title: 1, summary: 1, content: 1, 'author.name': 1 })
    .toArray();
  res.render('posts-list', { posts: posts });
});

router.get('/new-post', async function (req, res) {
  const authors = await db.getDb().collection('authors').find().toArray();
  res.render('create-post', { authors: authors });
});

router.post('/new-post', upload.single('image'), async function (req, res) {
  const uploadedImageFile = req.file;

  const authorId = new ObjectId(req.body.author);
  const author = await db
    .getDb()
    .collection('authors')
    .findOne({ _id: authorId });

  const enteredTitle = req.body.title;
  const enteredSummary = req.body.summary;
  const enteredContent = req.body.content;
  const date = new Date();
  const selectedAuthor = {
    author: {
      id: authorId,
      name: author.name,
      email: author.email
    }
  };
  const selectedImage = uploadedImageFile.path;

  const post = new Post(
    enteredTitle,
    enteredSummary,
    enteredContent,
    date,
    selectedAuthor.author,
    selectedImage
  );
  await post.save();

  res.redirect('/posts');
});

router.get('/blog/:id/edit', async function (req, res) {
  const postId = req.params.id;
  const post = await db
    .getDb()
    .collection('posts')
    .findOne(
      { _id: new ObjectId(postId) },
      { title: 1, summary: 1, content: 1 }
    );

  if (!post) {
    return res.status(404).render('404');
  }

  res.render('update-post', { post: post });
});

router.post('/blog/:id/edit', async function (req, res) {
  const enteredTitle = req.body.title;
  const enteredSummary = req.body.summary;
  const enteredContent = req.body.content;

  const post = new Post(
    enteredTitle,
    enteredSummary,
    enteredContent,
    req.params.id
  );
  await post.save();

  res.redirect('/posts');
});

router.post('/blog/:id/delete', async function (req, res) {
  const postId = new ObjectId(req.params.id);
  const result = await db
    .getDb()
    .collection('posts')
    .deleteOne({ _id: postId });
  res.redirect('/posts');
});

router.get('/admin', async function (req, res) {
  if (!res.locals.isAuth) {
    // if (!req.session.user)
    return res.status(401).render('401');
  }

  if (!res.locals.isAdmin) {
    return res.status(403).render('403');
  }

  res.render('admin');
});

module.exports = router;

models\post.js:

const mongodb = require('mongodb');

const db = require('../data/database');

const ObjectId = mongodb.ObjectId;

class Post {
  constructor(title, summary, content, date, author, image, id) {
    this.title = title;
    this.summary = summary;
    this.content = content;
    this.date = date;
    this.author = author;
    this.image = image;

    if (id) {
      this.id = new ObjectId(id);
    }
  }

  async save() {
    let result;
    if (this.id) {
      result = await db
        .getDb()
        .collection('posts')
        .updateOne(
          { _id: postId },
          {
            $set: {
              title: this.id,
              summary: this.summary,
              content: this.content
            }
          }
        );
    } else {
      result = await db.getDb().collection('posts').insertOne({
        title: this.title,
        summary: this.summary,
        content: this.content,
        date: this.date,
        author: this.author,
        imagePath: this.image
      });
    }

    return result;
  }
}

module.exports = Post;

CodePudding user response:

You're passing id as a wrong argument to when creating post object under /blog/:id/edit route, it doesn't get picked up, so it saves the post, instead of updating it, making other properties undefined.

Try this, pass 3 undefined arguments before id, which is passed as the last argument under /blog/:id/edit route:

  const post = new Post(
    enteredTitle,
    enteredSummary,
    enteredContent,
    ...[,,,], // pass 3 undefined arguments
    req.params.id
  );
  await post.save();
  • Related