Reputation: 563
I've created a simple todo checklist website, where users can view "Today's"
todo lists (on Home page), add new items to the list, create and delete other todo lists. All my code worked perfectly.
So I decided to refactor the code to separate the Mongoose (MongoDB) code into a separate module called model.js
, from the app.js
file (where the HTTP methods are handled).
Now, my Home page still loads on the browser, however, Nodemon shows that the app crashes in the terminal. If I select any option on the website, the page just doesn't display anything. So since my app is crashing on the Home route, I've placed console.log
statements within app.get('/' ...
api, and any methods it calls from the model.js
file, in order to try to debug the issue, but to no avail.
Before I share my code, just to note that I only shared the app.get('/' ...
and app.post('/'...
code from app.js
file, since the app is crashing on the Home page, and all of the code from the model.js
file.
Here's my code:
app.js
const express = require('express');
const bodyParser = require('body-parser');
const date = require(`${__dirname}\\date.js`);
const model = require(`${__dirname}\\model.js`);
const ejs = require('ejs');
const _ = require('lodash');
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.set('view engine', 'ejs');
app.use(express.static('public'));
//*** HTTP requests' handlers
app.get('/', (req, res) => {
const defaultListName = "Today's";
const todayFormatted = date.getDate();
model.DBUtility.findOneList(defaultListName, (foundList) => {
console.log('inside model.DBUtility.findOneList');
if (!foundList) {
console.log('inside !foundList. Just before createNewListInDB');
model.DBUtility.createNewListInDB(defaultListName);
res.redirect('/');
} else {
res.render('list', {
listTitlePassToH1: foundList.name,
todayDate: todayFormatted,
listItems: foundList.items
});
console.log(`else render list. found list: ${foundList.name}`);
}
console.log('end of model.DBUtility.findOneList')
});
console.log(`end of app.get('/'`);
});
app.post('/', (req, res) => {
const receivedItem = req.body.newItem;
const listName = req.body.listName;
model.DBUtility.addNewItemToListInDB(listName, receivedItem);
if (listName === "Today's") {
res.redirect('/');
} else {
res.redirect('/' + listName);
}
});
const port = 3000;
app.listen(port, () => {
console.log(`Server started, listening on port: ${port}`);
});
model.js:
const mongoose = require('mongoose');
//create DB
mongoose.connect('mongodb://localhost:27017/todolistDB', {useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false });
//create schema
const itemsSchema = new mongoose.Schema ({
name: String
});
const listSchema = new mongoose.Schema ({
name: String,
items: [itemsSchema]
});
const Item = mongoose.model('Item', itemsSchema);
const List = mongoose.model('List', listSchema);
class DBUtility {
constructor() {
console.log("Constructor class DBUtility");
}
// *** DB utility methods
static findOneList(listName, callBack) {
List.findOne({name: listName}, (err, foundList) => {
if (err) {
console.log(err);
} else {
console.log(`Found list: ${foundList.name}`);
var list = foundList;
}
callBack(list);
});
}
static findAllLists(callBack) {
List.find({}, (err, allLists) => {
if (err) {
console.log(err);
} else {
console.log(`Found all lists: ${allLists}`);
callBack(allLists);
}
});
}
static createNewListInDB(listName) {
//create default items for new list
const introItem = new Item({
name: 'Welcome to your todolist!'
});
const addItem = new Item({
name: 'Hit the + button to add a new item.'
});
const deleteItem = new Item({
name: '<-- Hit this to delete an item.'
});
const listNameCap = _.capitalize(listName);
const list = new List({
name: listNameCap,
items: [introItem, addItem, deleteItem]
});
list.save(err => {
if (err) return handleError(err);
console.log(`List ${list} added successfully to DB.`);
});
}
static addNewItemToListInDB(listName, itemName) {
console.log(`List passed: ${listName}`);
List.findOne({name: listName}, (err, foundList) => {
if (err) {
console.log(err);
} else {
foundList.items.push({ name: itemName });
foundList.save(err => {
if (err) return handleError(err);
console.log(`Item ${itemName} added to list ${listName} successfully.`);
});
}
});
}
static deleteItemFromListInDB(listName, itemID) {
// findOneAndUpdate is a mongoose method, but $pull is a mongoDB method
List.findOneAndUpdate({name: listName}, {$pull: {items: {_id: itemID}}}, (err, updatedList) => {
if (err) {
console.log(err);
} else {
console.log(`deleted item "${itemID}" from list ${updatedList} successfully.`);
console.log(`Updated list: ${updatedList}.`);
}
});
}
static deleteOneListFromDB(listID) {
List.findByIdAndDelete(listID, (err, deletedList) => {
if (err) {
console.log(err);
} else {
console.log(`Deleted list ${deletedList.name} successfully`);
}
});
}
}
exports.DBUtility = DBUtility;
Here's the logs and error I get in Terminal:
Server started, listening on port: 3000
end of app.get('/'
Found list: Today's
inside model.DBUtility.findOneList
else render list. found list: Today's
end of model.DBUtility.findOneList
events.js:288
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'name' of null
Note, I have only one LIST object stored in MongoDB database, which is "Today's" todo list:
db.lists.find().pretty()
{
"_id" : ObjectId("5e98982a7f8b083a6cf2d433"),
"name" : "Today's",
"items" : [
{
"_id" : ObjectId("5e98982a7f8b083a6cf2d430"),
"name" : "Welcome to your todolist!"
},
{
"_id" : ObjectId("5e98982a7f8b083a6cf2d431"),
"name" : "Hit the + button to add a new item."
},
{
"_id" : ObjectId("5e98982a7f8b083a6cf2d432"),
"name" : "<-- Hit this to delete an item."
}
],
"__v" : 0
}
When I deleted this object from MongoDB terminal using:
> db.lists.deleteOne( {name: "Today's"} )
{ "acknowledged" : true, "deletedCount" : 1 }
Now even my Home page doesn't load anymore. I get:
This site can’t be reached
And the error in Terminal is the same, however, I'm only seeing the very last console.log statement in app.get('/'...
Server started, listening on port: 3000
end of app.get('/'
events.js:288
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'name' of null
Also, model.js and app.js are on the same directory level:
Sorry for the long-winded post, but I wanted to make sure I'm thorough in my explanation.
EDIT Here's the full error message :
[nodemon] restarting due to changes...
[nodemon] restarting due to changes...
[nodemon] starting `node app.js`
Server started, listening on port: 3000
end of app.get('/'
events.js:288
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'name' of null
at C:\Users\hazzaldo\Desktop\...\todolist-v2\model.js:36:54
at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\model.js:4849:16
at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\model.js:4849:16
at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\model.js:4872:21
at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\query.js:4379:11
at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\kareem\index.js:135:16
at processTicksAndRejections (internal/process/task_queues.js:79:11)
Emitted 'error' event on Function instance at:
at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\model.js:4851:13
at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
[... lines matching original stack trace ...]
at processTicksAndRejections (internal/process/task_queues.js:79:11)
[nodemon] app crashed - waiting for file changes before starting...
Upvotes: 0
Views: 898
Reputation: 11283
Error
TypeError: Cannot read property 'name' of null
happens when you try to access 'name' property of a null object.
So the only candidates where error appears follow obj.name
pattern.
Now when I think about it, another case would be name
property destructuring but it if natively supported it gives another error.
Also
console.log('inside !foundList. Just before createNewListInDB');
model.DBUtility.createNewListInDB(defaultListName);
res.redirect('/');
function model.DBUtility.createNewListInDB(defaultListName);
is asynchronous and after it you call res.redirect('/')
.
I don't know how Node Express
handles this function internally but you should redirect after tasks were created.
Like
model.DBUtility.createNewListInDB(defaultListName)
.then(_ => {
res.redirect('/');
});
--Edit
list
variable wasn't declared outside if statement and could raise exception list is not defined
static findOneList(listName, callBack) {
var list = null;
List.findOne({name: listName}, (err, foundList) => {
if (err) {
console.log(err);
} else {
console.log(`Found list: ${foundList.name}`);
list = foundList;
}
callBack(list);
});
}
Also, const _ = require("lodash");
was not included inside model.js
. As a lodash
method was used inside: static createNewListInDB(listName)
method. Line: const listNameCap = _.capitalize(listName);
Upvotes: 1
Reputation: 4983
You are getting error on line no 36 of model.js
console.log(`Found list: ${foundList.name}`)
you can update your code like
static findOneList(listName, callBack) {
List.findOne({name: listName}, (err, foundList) => {
if (err || !foundList) {
console.log(err);
} else {
console.log(`Found list: ${foundList.name}`);
var list = foundList;
}
callBack(list);
});
}
Upvotes: 0