1

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 class="post-item">
    2|   <h2><%= post.title %></h2>
 >> 3|   <p class="post-item-author">Rédigé par <%= post.author.name %></p>
    4|   <p><%= post.summary %></p>
    5|   <div class="post-actions">
    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;

1 Answer 1

1

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();
Sign up to request clarification or add additional context in comments.

5 Comments

As you can see in my code in routes\admin\blog.js:, I do pass ìd as the last argument under /blog/:id/edit route => 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'); });
to the route handler, not the post. try my code
Yes, I did put it as the last argument, like this: const post = new Post( enteredTitle, enteredSummary, enteredContent, req.params.id ); await post.save();
yes, but it's on the wrong position, it's being passed as a date. did you try my code?
Yes, that's it. I tried your code and it works fine now!

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.