1

I've exhausted my own testing and searched several similar but not exact issues on stack overflow. I hope/dread that this is a simple error that I'm just not catching. This program lets you make a list of movies. When I submit a movie, I get the error:

Cannot read property 'listTitle' of null TypeError: Cannot read property 'listTitle' of null at module.exports.addMovie (/Users/---/Documents/movieInventoryApp/controllers/movies.js:73:22) at processTicksAndRejections (internal/process/task_queues.js:95:5)

The parameter list appears to be empty when it reaches the controller. I verified the following:

  • The list ID is undefined at the router (I used a test print middleware to check)
  • The program gets to the controller. The code seems to work, it's just not sending the list._id or any other parameters
  • I've checked and rechecked for typos, and I've compared it line for line to a file with similar functionality that works perfectly. I also tried updating all variables to be more clear.
  • my middleware validators do not throw errors and don't seem to be interfering (tested methods without them)

Github link: (adapted from a Udemy course project) https://github.com/mrsbluerose/movieInventoryApp/tree/wip-add-lists

Here is my code from a few files that include test print statements and a note on what they result in:

show.ejs for list (part of form that submits)

<div class="col-6">
        <% if(currentUser) { %>
        <h2>Add a Movie</h2>
        <form class="mb-3 needs-validation" action="/lists/<%= list._id %>/movies" method="POST" novalidate>
          <% console.log(list._id) //Print Test prints correct id%>
          <div class="mb-3">
            <label for="movieTitle">Movie Title</label>
            <input type="text" name="movie[movieTitle]" id="movieTitle">
          </div>
          <div class="mb-3">
            <label for="movieDescription">Description</label>
            <input type="text" name="movie[movieDescription]" id="movieDescription" required>
          </div>
          <button class="btn btn-success">Add Movie</button>
        </form>
        <% } %>
      </div>
    </div> 

app.js (part that sends to routes)

app.use('/lists', listRoutes);
app.use('/lists/:id/movies', movieRoutes); //this appears to route 
app.use('/', userRoutes);

movies.js router

const express = require('express');
const router = express.Router();
const movies = require('../controllers/movies');
const catchAsync = require('../utils/catchAsync');
//const Movie = require('../models/movie');
const { isLoggedIn, validateMovie, isAuthor, testPrint } = require('../middleware');

// removed commented out code

router.post('/', isLoggedIn, validateMovie, /*testPrint,*/ catchAsync(movies.addMovie)); //Test Print: params did not make it from the router

router.delete('/:movieId', isLoggedIn, isAuthor, catchAsync(movies.deleteMovie));

module.exports = router;

movies.js controller

const Movie = require('../models/movie');
const List = require('../models/list');

// removed commented out code

module.exports.addMovie = async (req, res) => {
    console.log( req.params ) ;//////Print Test: empty params
    const { id } = req.params;
    console.log(id);///////Print Test: undefined
    const list = await List.findById(req.params.id);
    console.log(list.listTitle);////////Print Test: undefined
    const movie = new Movie(req.body.movie);
    console.log(movie.movieTitle);////////Print Test: undefined
    movie.movieAuthor = req.user._id;
    list.listOfMovies.push(movie);
    await movie.save();
    await list.save();
    req.flash('success', 'New review added!');
    res.redirect(`/lists/${list._id}`);
}

module.exports.deleteMovie = async (req, res) => {
    const { id, movieId } = req.params;
    await List.findByIdAndUpdate(id, { $pull: { movieList: movieId } });
    await Movie.findByIdAndDelete(MovieId);
    req.flash('success', 'Successfully deleted movie')
    res.redirect(`/lists/${id}`);
}

List Router:

const express = require('express');
const router = express.Router();
const lists = require('../controllers/lists');
const catchAsync = require('../utils/catchAsync');
const { isLoggedIn } = require('../middleware');

router.route('/')
    .get(catchAsync(lists.index))
    .post(catchAsync(lists.createList))

router.get('/new', isLoggedIn, lists.renderNewForm);

router.route('/:id')
    .get(catchAsync(lists.showList))
    .put(catchAsync(lists.updateList))
    .delete((lists.deleteList))

router.get('/:id/edit', catchAsync(lists.renderEditForm));

module.exports = router;

List Controller:

const { register } = require('../models/list');
const List = require('../models/list');
//const Movie = require('../models/movie');

module.exports.index = async (req, res) => {
    const unsortedLists = await List.find({});
    const lists = unsortedLists.sort((a,b) => {
        if (a.listTitle < b.listTitle) {
            return -1;
        }
        if (a.listTitle > b.listTitle) {
            return 1;
        }
        return 0;
    });
    res.render('lists/index', { lists })
}

module.exports.renderNewForm = (req, res) => {
    res.render('lists/new');
}

module.exports.createList = async (req, res, next) => {
    const list = new List(req.body.list);
    list.listAuthor = req.user._id;
    //const movie = new Movie({title: 'title', description: 'description'});
    //list.movies.push(movie);
    await list.save();
    req.flash('success', 'Successfully made a new list!');
    res.redirect(`/lists/${list._id}`)
}

// removed commented out code

module.exports.showList = async (req, res,) => {
    const list = await List.findById(req.params.id).populate({ path:'listOfMovies' }).populate('listAuthor');
    if (!list) {
        req.flash('error', 'Cannot find that list!');
        return res.redirect('/lists');
    }
    res.render('lists/show', { list });
}

module.exports.renderEditForm = async (req, res) => {
    const { id } = req.params;
    const list = await List.findById(id)
    if (!list) {
        req.flash('error', 'Cannot find that list!');
        return res.redirect('/lists');
    }
    res.render('lists/edit', { list });
}

module.exports.updateList = async (req, res) => {
    const { id } = req.params;
    const movie = await List.findByIdAndUpdate(id, { ...req.body.list });
    req.flash('success', 'Successfully updated list!');
    res.redirect(`/lists/${list._id}`)
}

module.exports.deleteList = async (req, res) => {
    const { id } = req.params;
    await List.findByIdAndDelete(id);
    req.flash('success', 'Successfully deleted list')
    res.redirect('/lists');
}
robertklep
  • 198,204
  • 35
  • 394
  • 381
Audra W.
  • 19
  • 3
  • The linked question and answer fixed my problem. Thanks!! Needed const router = express.Router( { mergeParams: true} ). robertklep, -- you don't even know how much you helped!! :-) – Audra W. Sep 08 '22 at 19:37

0 Answers0