1

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

I'm trying to use the MVC pattern to improve my website.

However, after creating a model, I can't access some pages and I get the following error message on my terminal:

TypeError: C:\Users\DELL\Desktop\node-com4muz-database-mvc\views\home\posts.ejs:19 17| 18| <% for (const post of posts) { %>

19|

  • <%- include('includes/posts/post-item', { post: post }) %>
  • 20| <% } %> 21| 22| <% } %>

    C:\Users\DELL\Desktop\node-com4muz-database-mvc\views\home\includes\posts\post-item.ejs:4 2| " alt="" /> 3|

    <%= post.title %>

    4| By <%= post.author.name %>

    5|

    <%= post.summary %>

    6| 7| ">View Post

    Cannot read properties of undefined (reading 'name') at eval ("C:\Users\DELL\Desktop\node-com4muz-database-mvc\views\home\includes\posts\post-item.ejs":20: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\home\posts.ejs":29:17) at posts (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\home\posts.ejs' }

    I would like someone to help me find where is the issue in my code by comparing both versions: before the MVC pattern and after the MVC pattern.

    Before the MVC pattern:

    routes\admin\blog.js from line 37 to line 62:

    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 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 newPost = {
        title: req.body.title,
        summary: req.body.summary,
        body: req.body.content,
        date: new Date(),
        author: {
          id: authorId,
          name: author.name,
          email: author.email
        },
        imagePath: uploadedImageFile.path
      };
    
      const result = await db.getDb().collection('posts').insertOne(newPost);
      // console.log(result);
      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, body: 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,
              body: 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;
    

    After the MVC pattern:

    routes\admin\blog.js from line 69 to line 72:

    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,
        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, body: 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,
              body: 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;
    

    Indeed, I have created a model to use the post route where I create new post. Here is the code I wrote:

    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.autor = 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;
    

    Before, I changed the code from the first version before the MVC pattern, the website was working fine.

    Now, the error says that post.author.name in views\home\includes\posts\post-item.ejs is not defined anymore.

    Please find the code related to view the blog page:

    views\home\posts.ejs:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <%- include('../admin/includes/head', { title: 'Blog - Tous Les Articles' })
        %>
        <link rel="stylesheet" href="/admin/styles/posts.css" />
      </head>
    
      <body>
        <%- include('../admin/includes/header') %>
        <main id="all-posts">
          <h1>Nos Articles</h1>
          <% if (!posts || posts.length === 0) { %>
          <p>Aucun article trouvé - peut-être voulez-vous en créer un ?</p>
          <a class="btn" href="/new-post">Create a new Post</a>
          <% } else { %>
          <ol id="posts-list">
            <% for (const post of posts) { %>
            <li><%- include('includes/posts/post-item', { post: post }) %></li>
            <% } %>
          </ol>
          <% } %>
        </main>
      </body>
    </html>
    

    views\home\includes\posts\post-item.ejs:

    <article class="post-item">
      <img src="<%= post.imagePath %>" alt="<%= post.title %>" />
      <h2><%= post.title %></h2>
      <p class="post-item-author">By <%= post.author.name %></p>
      <p><%= post.summary %></p>
      <div class="post-actions">
        <a class="btn" href="/blog/<%= post._id %>">View Post</a>
      </div>
    </article>
    
    13
    • this.autor = author; => this.author = author; Commented Nov 19, 2022 at 12:58
    • What do you mean? Commented Nov 19, 2022 at 13:37
    • you have a typo there, causing author to be undefined.. Commented Nov 19, 2022 at 13:41
    • I thought it's self-explanatory.. it's not autor, it's author.. add letter h, it's in th Post constructor Commented Nov 19, 2022 at 13:50
    • Yes, I got it now. I changed it, but I still get the same error. Commented Nov 19, 2022 at 13:54

    1 Answer 1

    1

    I fixed this issue by calling the author object on the selectedAuthor constant as below at line 28:

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

    Comments

    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.