The MVC (Model-View-Controller) pattern is a software architectural pattern commonly used in web development to separate the concerns of an application into three distinct components: the model, the view, and the controller. Node.js is a versatile runtime environment that you can use to implement the MVC pattern. In this topic, you will learn about each component of MVC and see a small example that implements the MVC pattern.
Let's start by briefly checking each component in the context of Node.js.
Model
The model represents the data and business logic of the application. It encapsulates the application's data structure, access methods, and validation rules. In Node.js, you can define your models using frameworks like Mongoose (for MongoDB) or Sequelize (for SQL databases). These frameworks provide an abstraction layer to interact with the database and define models with properties, relationships, and methods.
Here's a model example with plain JavaScript:
const books = [];
class Book {
constructor(title, author) {
this.title = title;
this.author = author;
}
static getAllBooks() {
return books;
}
static addBook(book) {
books.push(book);
}
}
module.exports = Book;
In this example, we define a Book class with a constructor that takes in title and author as parameters. The constructor assigns these values to the respective properties of the Book instance. Also, the Bookclass provides static methods to get all books and add a new book. Finally, we export the Book model so that it is usable in a controller.
View
The view is responsible for rendering the user interface and presenting data. In Node.js, you can use templating engines like Handlebars, EJS, or Pug, which are available as Node.js packages, to generate dynamic HTML pages. Views are typically separate files that contain HTML markup with placeholders for dynamic data. The controller, which you will learn about in the next section, passes data to the view, and the view will render it accordingly. For now, think of controller as the bridge between the data(model) and the view.
Continuing from the previous example, here's the example for view:
<!-- Book View -->
<!DOCTYPE html>
<html>
<head>
<title>Book List</title>
</head>
<body>
<h1>Book List</h1>
<div id="bookList">
<p>No books available.</p>
</div>
<form id="addBookForm">
<input type="text" id="titleInput" placeholder="Book Title" required>
<input type="text" id="authorInput" placeholder="Author" required>
<button type="submit">Add Book</button>
</form>
<script src="controller.js"></script>
</body>
</html>
The view is an HTML file (book.html) with placeholders for the book list and the form inputs. The snippet above contains id attributes associated with relevant elements to allow us to select them in JavaScript.
Controller
The controller acts as an intermediary between the model and the view. It receives user input from the view, interacts with the model to retrieve or modify data, and updates the view accordingly. In Node.js, you can define your controllers as separate modules or classes that handle specific routes or actions. The controller is responsible for processing incoming requests, validating data, and coordinating the flow of information between the model and the view.
Take a look at the following controller example for the Book model:
const Book = require('./book');
const bookListContainer = document.getElementById('bookList');
const addBookForm = document.getElementById('addBookForm');
const titleInput = document.getElementById('titleInput');
const authorInput = document.getElementById('authorInput');
function renderBookList() {
const books = Book.getAllBooks();
bookListContainer.innerHTML = '';
if (books.length === 0) {
bookListContainer.innerHTML = '<p>No books available.</p>';
} else {
const ul = document.createElement('ul');
books.forEach(book => {
const li = document.createElement('li');
li.textContent = `${book.title} by ${book.author}`;
ul.appendChild(li);
});
bookListContainer.appendChild(ul);
}
}
addBookForm.addEventListener('submit', event => {
event.preventDefault();
const title = titleInput.value;
const author = authorInput.value;
const book = new Book(title, author);
Book.addBook(book);
renderBookList();
titleInput.value = '';
authorInput.value = '';
});
renderBookList();
The controller (controller.js) renders the book list. It handles the function of adding a new book when the form is submitted and updating the view accordingly. It also listens for the submit event on the form and prevents the default form submission behavior. Finally, it creates a new Book instance, adds it to the model, and re-renders the book list.
To run this application, you can open the book.html file in a web browser. The book list will be displayed and you can use the form to add new books.
Conclusion
Overall, the MVC pattern helps organize code, separate concerns, and promote code reusability and maintainability in Node.js applications. You've looked at each component that forms the MVC pattern: The model that contains the data of the application, the view that displays the data, and the controller that acts as a bridge between the model and view. To understand how these three components are related, you've looked at a Book management example using the MVC pattern.