" MVC is an
acronym for Model-View-Controller "
It is a design pattern for software projects. It is used
majorly by Node developers and by C#, Ruby, PHP framework users too. In MVC
pattern, application and its development are divided into three interconnected
parts. The advantage of this is it helps in focusing on a specific part of the
application name, the ways information is presented to and accepted from, the
user. It helps in allowing for efficient code reuse and the parallel
development of the application.
Model:
Model represents the structure of data, the format, and the constraints with
which it is stored. It maintains the data of the application. Essentially, it
is the database part of the application.
View:
View is what is presented to the user. Views utilize the Model and present data
in a form in which the user wants. A user can also be allowed to make changes
to the data presented to the user. They consist of static and dynamic pages
that are rendered or sent to the user when the user requests them.
Controller:
Controller controls the requests of the user and then
generates an appropriate response which is fed to the viewer. Typically, the user
interacts with the View, which in turn generates the appropriate request; this
request will be handled by a controller. The controller renders the appropriate
view with the model data as a response.
So,
to sum it up:
· Model
is data part.
· View
is User Interface part.
· Controller
is request-response handler.
In short these are the
steps:
- A Request is sent to the server and handled by the application controllers.
- Express.js and its routes can behave like controllers handling requests and determining whether to process the request further or send back a response.
- The routes may also trigger more code logic in other modules, communicate with database, or render a view.
- The client receives a response after the controller decides to generate views to send back.
Implementation
of MVC
Step-1: First, initialize
npm in a directory of your choice
npm init
Then accept all the
default values by pressing the enter key. It will create the package.json file.
Step-2: Now we will install the Express framework and the basic
packages for our application.
npm i express body-parser mongoose
express-handlebars --save
- express: Fast,
efficient, minimalist web framework for node.
- body-parser: Node.js body parsing middleware.
- express-handlebars: A Handlebars view engine for Express.
- mongoose: Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.
After installing
let’s have a look at the project setup.
Step-3: Project Setup- We have folders Model, View, Controller in our project, the directory structure is as follows:
Step-4: Create a database in MongoDB using the following steps:
- Open Mongod Server
- Open MongoDB Compass Community
- Create database "studentDB" and a collection "students"
Step-5: Now create a folder in your application folder as "Models" and then create a file db.js for creating a connection with the MongoDB database.
db.js
const mongoose = require('mongoose');
mongoose.connect(
' mongodb://127.0.0.1:27017/studentDB',
{
useNewUrlParser: true,
useUnifiedTopology: true,
},
(err) => {
if (!err) {
console.log('MongoDB Connection Successful');
} else {
console.log('Error in Connection:' + err);
}
}
);
Step-6: Now create another file student.model.js in "Models" folder for creating a schema for the database, this will be used as a data-structure for getting data from the client and save in the database.
student.model.js
const mongoose = require('mongoose');
var studentSchema = new mongoose.Schema({
fullName: {
type: String,
},
email: {
type: String,
},
mobile: {
type: String,
},
city: {
type: String,
},
registerOn: {
type: Date,
default: Date.now,
},
});
mongoose.model('Student', studentSchema);
Step-7: Now create express app in file server.js that listen at Port 3000.
server.js
// Importing Models in Express App
require('./models/db');
const express = require('express');
// App Port
const app = express();
app.listen(3000, () => console.log('App started at 3000'));
Step-8: Now create another folder in your application folder as "Controllers" and then create a file studenController.js for creating routes of the application.
studenController.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.send('sample Text');
});
module.exports = router;
Step-9: Update server.js by importing controller and router middleware.
server.js
// import controller
require('./models/db');
const studentController = require('./controller/studentController');
// Router Middleware
app.use('/student', studentController);
Output in Browser- http://localhost:3000/student
Step-10: Now update server.js for Configuring Express Handlebars.
server.js
// Import Express Handlerbar
const path = require('path');
const exphbs = require('express-handlebars');
// Configure View Engine
app.set('views', path.join(__dirname, '/views/'));
app.engine(
'hbs',
exphbs({
extname: 'hbs',
defaultLayout: 'mainLayout',
layoutsDir: __dirname + '/views/layouts/',
})
);
app.set('view engine', 'hbs');
Step-11: For Views, create another folder in your application folder as "views" and then create folder "layouts" inside, that
store our main layout, Create a file mainLayout.hbs.
mainLayout.hbs
<!DOCTYPE html>
<html lang="en">
<head>
<title>NodeJS Express MongoDB CRUD</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body class="bg-secondary">
<div class="row">
<div class="col-md-8 offset-md-2" style="background-color: white; padding:20px; margin-top:25px;">
{{!-- this inherit our view --}}
{{{body}}}
</div>
</div>
</body>
</html>
Step-12: For Views, create folder "student" in your "views" folder that stores our view. Create a file as addOrEdit.hbs handlebar.
addOrEdit.hbs
<h3>{{viewTitle}}</h3>
<form>
<div class="form-group">
<label>Full Name</label>
<input type="text" class="form-control" name="fullName" placeholder="Full Name">
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" name="email" placeholder="Email">
<div class="form-row">
<div class="form-group col-md-6">
<label>Mobile</label>
<input type="text" class="form-control" name="mobile" placeholder="Mobile">
</div>
<div class="form-group col-md-6">
<label>City</label>
<input type="text" class="form-control" name="city" placeholder="City">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-info"><i class="fa fa-database"></i> Submit</button>
</div>
</form>
Step-13: Now update studentController.js for rendering the above view.
studentController.js
const express = require('express');
const router = express.Router();
// render View
router.get('/', (req, res) => {
res.render('student/addOrEdit', {
viewTitle: 'Insert New Student',
});
});
Output in Browser- http://localhost:3000/student
Step-14: Now update the following changes in addOrEdit.hbs form Handlebars, server.js and in studentController.js for posting data.
addOrEdit.hbs
<form method="POST" action="/student" autocomplete="off">server.js
// Import body-parser package const bodyparser = require('body-parser');app.use( bodyparser.urlencoded({ extended: true, }));app.use(bodyparser.json());
studentController.js
router.post('/', (req, res) => {
console.log(req.body);});
Output in Nodemone server: This means data is Posting
[nodemon] starting `node server.js`
App started at 3000
MongoDB Connection Successful
{ fullName: 'Pankaj kapoor',
email: 'pankkap@gmail.com',
mobile: '9729280731',
city: 'Chandigarh' }
Step-15: Now update in studentController.js for posting data into the MongoDb database.
studentController.js
const mongoose = require('mongoose');require('./../models/student.model');const Student = mongoose.model('Student');
router.post('/', (req, res) => { insertRecord(req, res);});
function insertRecord(req, res) { var student = new Student(); student.fullName = req.body.fullName; student.email = req.body.email; student.mobile = req.body.mobile; student.city = req.body.city; student.save((err, docs) => { if (!err) { res.redirect('student/list'); } else { console.log('Error during Record Insertion...' + err); } });}router.get('/list', (req, res) => { res.json('Student List');});Output in Browser- http://localhost:3000/student : Insert your student Data in the Form and press Submit Button, You will be redirected to /student/list display the sample ouput as Student List (This view will be update in the next session). At the same time, data will also be stored in the MongoDB.
Step-16: Now update in studentController.js for reading the list of student's records from MongoDb database and display in /student/list router.
studentController.js
router.get('/list', (req, res) => { Student.find((err, docs) => { if (!err) { res.render('student/list', { list: docs, }); } else { console.log('Error in retrieving employee list :' + err); } });});
Step-17: Now we have to create a new express handlebar that will display the list of students' records.
list.hbs
<h3>Student List</h3><table class="table table-striped"> <thead> <tr> <th>Full Name</th> <th>Email</th> <th>Mobile</th> <th>City</th> <th></th> </tr> </thead> <tbody> {{#each list}} <tr> <td>{{this.fullName}}</td> <td>{{this.email}}</td> <td>{{this.mobile}}</td> <td>{{this.city}}</td> <td> <a><i class="fa fa-pencil fa-lg " aria-hidden="true"></i></a> <a><i class="fa fa-trash fa-lg" aria-hidden="true"></i></a> </td> </tr> {{/each}} </tbody></table>
Step-18: Now we add two buttons in both views one for creating New Student and 2nd View List of students. list.hbs
<h3><a class="btn btn-secondary" href="/student"><i class="fa fa-plus"> </i> Create New</a> Student List</h3>
addOrEdit.hbs
<a class="btn btn-secondary" href="/student/list"><i class="fa fa-list-alt"> </i> View All</a>
Step-19: When using express Handlebar, there is an error in retrieving data from the database.
[nodemon] starting `node server.js`App started at 3000MongoDB Connection SuccessfulHandlebars: Access has been denied to resolve the property "fullName" because it is not an "own property" of its parent.
This issue can be resolved by adding the following code in server.js file.
const Handlebars = require('handlebars');const { allowInsecurePrototypeAccess,} = require('@handlebars/allow-prototype-access');
// Configure View Engineapp.set('views', path.join(__dirname, '/views/'));app.engine( 'hbs', exphbs({ extname: 'hbs', defaultLayout: 'mainLayout', layoutsDir: __dirname + '/views/layouts/',
// Issue in Access has been denied to resolve the property //"---" because it is not an "own property" of its parent. handlebars: allowInsecurePrototypeAccess(Handlebars), }));app.set('view engine', 'hbs');
Output in Browser- http://localhost:3000/student/list
Step-20: Now we create a new route in studentController.js for Edit Button to update the previously stored record.
studentController.js: This will open the view for update the previous record with ViewTitle proper: Update student
router.get('/:id', (req, res) => { Student.findById(req.params.id, (err, doc) => { if (!err) { res.render('student/addOrEdit', { viewTitle: 'Update Student', student: doc, }); } });});
Note: For step-20: Updating Record in the application following changes needs to be updated.
list.hbs
<td> <a href="/student/{{this._id}}"><i class="fa fa-pencil fa-lg " aria-hidden="true"></i></a> <a><i class="fa fa-trash fa-lg" aria-hidden="true"></i></a></td>
addOrEdit.hbs <h3>{{viewTitle}}</h3><form method="POST" action="/student" autocomplete="off"> <input type="hidden" name="_id" value="{{student._id}}"> <div class="form-group"> <label>Full Name</label> <input type="text" class="form-control" name="fullName" placeholder="Full Name" value="{{student.fullName}}"> <div class="form-group"> <label>Email</label> <input type="text" class="form-control" name="email" placeholder="Email" value="{{student.email}}"> <div class="form-row"> <div class="form-group col-md-6"> <label>Mobile</label> <input type="text" class="form-control" name="mobile" placeholder="Mobile" value="{{student.mobile}}"> </div> <div class="form-group col-md-6"> <label>City</label> <input type="text" class="form-control" name="city" placeholder="City" value="{{student.city}}"> </div> </div> <div class="form-group"> <button type="submit" class="btn btn-info"><i class="fa fa-database"></i> Submit</button> <a class="btn btn-secondary" href="/student/list"><i class="fa fa-list-alt"></i> View All</a> </div></form>
studentController.js: Now we need to also update the following changes in this file also in order to save the updated record.
Important: Replace the previous post request with the following code
router.post('/', (req, res) => { if (req.body._id == '') insertRecord(req, res); else updateRecord(req, res);});
function updateRecord(req, res) { Student.findOneAndUpdate( { _id: req.body._id }, req.body, { new: true }, (err, doc) => { if (!err) { res.redirect('student/list'); } else { console.log('Error during record update : ' + err); } } );}
Output in Browser- http://localhost:3000/student/list
Step-21: Now we create a new route in studentController.js for Delete Button to delete the previously stored record.
studentController.js
router.get('/delete/:id', (req, res) => { Student.findByIdAndRemove(req.params.id, (err, doc) => { if (!err) { res.redirect('/student/list'); } else { console.log('Error in student delete :' + err); } });});
Step-10: Now update server.js for Configuring Express Handlebars.
server.js
// Import Express Handlerbar
const path = require('path');
const exphbs = require('express-handlebars');
// Configure View Engine
app.set('views', path.join(__dirname, '/views/'));
app.engine(
'hbs',
exphbs({
extname: 'hbs',
defaultLayout: 'mainLayout',
layoutsDir: __dirname + '/views/layouts/',
})
);
app.set('view engine', 'hbs');
Step-11: For Views, create another folder in your application folder as "views" and then create folder "layouts" inside, that
store our main layout, Create a file mainLayout.hbs.
mainLayout.hbs
<!DOCTYPE html>
<html lang="en">
<head>
<title>NodeJS Express MongoDB CRUD</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body class="bg-secondary">
<div class="row">
<div class="col-md-8 offset-md-2" style="background-color: white; padding:20px; margin-top:25px;">
{{!-- this inherit our view --}}
{{{body}}}
</div>
</div>
</body>
</html>
<h3>{{viewTitle}}</h3>
<form>
<div class="form-group">
<label>Full Name</label>
<input type="text" class="form-control" name="fullName" placeholder="Full Name">
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" name="email" placeholder="Email">
<div class="form-row">
<div class="form-group col-md-6">
<label>Mobile</label>
<input type="text" class="form-control" name="mobile" placeholder="Mobile">
</div>
<div class="form-group col-md-6">
<label>City</label>
<input type="text" class="form-control" name="city" placeholder="City">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-info"><i class="fa fa-database"></i> Submit</button>
</div>
</form>
Step-13: Now update studentController.js for rendering the above view.
studentController.js
studentController.js
const express = require('express');
const router = express.Router();
// render View
router.get('/', (req, res) => {
res.render('student/addOrEdit', {
viewTitle: 'Insert New Student',
});
});
Output in Browser- http://localhost:3000/student
Step-14: Now update the following changes in addOrEdit.hbs form Handlebars, server.js and in studentController.js for posting data.
addOrEdit.hbs
<form method="POST" action="/student" autocomplete="off">
server.js
// Import body-parser package
const bodyparser = require('body-parser');
app.use(
bodyparser.urlencoded({
extended: true,
})
);
app.use(bodyparser.json());
studentController.js
router.post('/', (req, res) => {
console.log(req.body);
});
[nodemon] starting `node server.js`
App started at 3000
MongoDB Connection Successful
{ fullName: 'Pankaj kapoor',
email: 'pankkap@gmail.com',
mobile: '9729280731',
city: 'Chandigarh' }
studentController.js
const mongoose = require('mongoose');
require('./../models/student.model');
const Student = mongoose.model('Student');
router.post('/', (req, res) => {
insertRecord(req, res);
});
function insertRecord(req, res) {
var student = new Student();
student.fullName = req.body.fullName;
student.email = req.body.email;
student.mobile = req.body.mobile;
student.city = req.body.city;
student.save((err, docs) => {
if (!err) {
res.redirect('student/list');
} else {
console.log('Error during Record Insertion...' + err);
}
});
}
router.get('/list', (req, res) => {
res.json('Student List');
});
Output in Browser- http://localhost:3000/student : Insert your student Data in the Form and press Submit Button, You will be redirected to /student/list display the sample ouput as Student List (This view will be update in the next session). At the same time, data will also be stored in the MongoDB.
Step-16: Now update in studentController.js for reading the list of student's records from MongoDb database
and display in /student/list router.
studentController.js
router.get('/list', (req, res) => {
Student.find((err, docs) => {
if (!err) {
res.render('student/list', {
list: docs,
});
} else {
console.log('Error in retrieving employee list :' + err);
}
});
});
Step-17: Now we have to create a new express handlebar that will display the list of students' records.
list.hbs
<h3>Student List</h3>
<table class="table table-striped">
<thead>
<tr>
<th>Full Name</th>
<th>Email</th>
<th>Mobile</th>
<th>City</th>
<th></th>
</tr>
</thead>
<tbody>
{{#each list}}
<tr>
<td>{{this.fullName}}</td>
<td>{{this.email}}</td>
<td>{{this.mobile}}</td>
<td>{{this.city}}</td>
<td>
<a><i class="fa fa-pencil fa-lg " aria-hidden="true"></i></a>
<a><i class="fa fa-trash fa-lg" aria-hidden="true"></i></a>
</td>
</tr>
{{/each}}
</tbody>
</table>
Step-18: Now we add two buttons in both views one for creating New Student and 2nd View List of students.
list.hbs
<h3><a class="btn btn-secondary" href="/student"><i class="fa fa-plus">
</i> Create New</a> Student List</h3>
addOrEdit.hbs
<a class="btn btn-secondary" href="/student/list"><i class="fa fa-list-alt">
</i> View All</a>
Step-19: When using express Handlebar, there is an error in retrieving data from the database.
[nodemon] starting `node server.js`
App started at 3000
MongoDB Connection Successful
Handlebars: Access has been denied to resolve the property "fullName" because it
is not an "own property" of its parent.
This issue can be resolved by adding the following code in server.js file.
const Handlebars = require('handlebars');
const {
allowInsecurePrototypeAccess,
} = require('@handlebars/allow-prototype-access');
// Configure View Engine
app.set('views', path.join(__dirname, '/views/'));
app.engine(
'hbs',
exphbs({
extname: 'hbs',
defaultLayout: 'mainLayout',
layoutsDir: __dirname + '/views/layouts/',
// Issue in Access has been denied to resolve the property
//"---" because it is not an "own property" of its parent.
handlebars: allowInsecurePrototypeAccess(Handlebars),
})
);
app.set('view engine', 'hbs');
Output in Browser- http://localhost:3000/student/list
Step-20: Now we create a new route in studentController.js for Edit Button to update the previously stored record.
studentController.js: This will open the view for update the previous record with ViewTitle proper: Update student
router.get('/:id', (req, res) => {
Student.findById(req.params.id, (err, doc) => {
if (!err) {
res.render('student/addOrEdit', {
viewTitle: 'Update Student',
student: doc,
});
}
});
});
Note: For step-20: Updating Record in the application following changes needs to be updated.
list.hbs
<td>
<a href="/student/{{this._id}}"><i class="fa fa-pencil fa-lg "
aria-hidden="true"></i></a>
<a><i class="fa fa-trash fa-lg" aria-hidden="true"></i></a>
</td>
addOrEdit.hbs
<h3>{{viewTitle}}</h3>
<form method="POST" action="/student" autocomplete="off">
<input type="hidden" name="_id" value="{{student._id}}">
<div class="form-group">
<label>Full Name</label>
<input type="text" class="form-control" name="fullName" placeholder="Full Name"
value="{{student.fullName}}">
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" name="email" placeholder="Email"
value="{{student.email}}">
<div class="form-row">
<div class="form-group col-md-6">
<label>Mobile</label>
<input type="text" class="form-control" name="mobile" placeholder="Mobile"
value="{{student.mobile}}">
</div>
<div class="form-group col-md-6">
<label>City</label>
<input type="text" class="form-control" name="city" placeholder="City"
value="{{student.city}}">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-info"><i class="fa fa-database"></i> Submit</button>
<a class="btn btn-secondary" href="/student/list"><i class="fa fa-list-alt"></i> View All</a>
</div>
</form>
studentController.js: Now we need to also update the following changes in this file also in order to save the updated record.
Important: Replace the previous post request with the following code
router.post('/', (req, res) => {
if (req.body._id == '')
insertRecord(req, res);
else
updateRecord(req, res);
});
function updateRecord(req, res) {
Student.findOneAndUpdate(
{ _id: req.body._id },
req.body,
{ new: true },
(err, doc) => {
if (!err) {
res.redirect('student/list');
} else {
console.log('Error during record update : ' + err);
}
}
);
}
Output in Browser- http://localhost:3000/student/list
studentController.js
router.get('/delete/:id', (req, res) => {
Student.findByIdAndRemove(req.params.id, (err, doc) => {
if (!err) {
res.redirect('/student/list');
} else {
console.log('Error in student delete :' + err);
}
});
});