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');
}