Updated on Oct 6, 2023
Before we continue with our app, there is one final module that we have to install. It is body-parser and can be installed with this command.
Next, let’s put together the application. We will start from the index.js file. Feel free to delete the “Hello World” code; we will replace it with our own. For each block of code we add, we will explain what it does. The block itself will contain a line that’s been commented out with slashes, which will provide a brief description as well. Let’s begin!
The first block will instruct Node.js to import the necessary modules (express and body-parser) and then create an Express application instance. The Express module is for building the web application, while body-parser is for parsing HTTP request bodies. Then, the last line creates the Express.js application, which will be used to configure and define routes for the web application. The sentence with the slashes in front of it is a comment that Node.js will not execute.
//dependencies required for the app
var express = require("express");
var bodyParser = require("body-parser");
var app = express();
The next block of code sets up body-parser as middleware to parse URL data from incoming HTTP POST requests. The two extended and true options also allow the middleware to parse more complex objects. In our case, body-parser will be able to access the name of the newtask (we talked about it earlier) typed in by the user on the client side and save it into an array on the server side. Simply put, when we type in anything in the newtask field, it will be saved into the appropriate array on the server.
Afterward, the app sets the view engine as EJS. It is the template engine we discussed earlier, and it will allow our app to render dynamically instead of statically when in our browser. Finally, the public directory is specified as the directory where the CSS file for the HTML template is. If you remember earlier, we talked about that file after installing EJS.
app.use(bodyParser.urlencoded({ extended: true }));
app.set("view engine", "ejs");
//render css files
app.use(express.static("public"));
Now that we have told the Node.js server to save the newtask input to an array, we have to initialize and define the array itself. With the next block of code, we will define arrays for complete and incomplete task items. We have placed a couple of examples so the app doesn’t appear empty when first opened.
//placeholders for added task
var task = [“buy socks”, “practise with nodejs”];
//placeholders for removed task
var complete = [“finish jquery”];
The next block of code is what will actually handle the addition of tasks to your to-do list. The first line after the comment defines a POST route to the /addtask URL path, which we specified earlier via the app object. The route will listen for incoming HTTP POST requests on the /addtask path and resolve them. Once such a request comes through, the next line actually extracts the data from it and stores it under the newTask variable. The newtask field, from earlier, is where we fill in the names of the to-do list items.
The last two lines after the second comment are what finalize the process and add your new to-do list item and refresh the page. The former line pushes the value of newTask into the task array, which adds the specified new task to the end of the array. Then, the latter line redirects the client’s browser back to the root URL (“/”), which will refresh the page and show your new, updated list.
//post route for adding new task
app.post("/addtask", function(req, res) {
var newTask = req.body.newtask;
//add the new task from the post route
task.push(newTask);
res.redirect("/");
});
Next, we have a similar block of code, but this one removes tasks from your to-do list and adds them to a list of completed tasks. The first and second lines in the code block work almost the same as in the previous block. They set up a new POST route using the app object, then listen for any HTTP POST requests on the /removetask URL path. When such a request comes through, the app extracts the data and stores it in the completeTask variable, representing the tasks needing removal.
Then, the code gets interesting as we are dealing with two different scenarios: removing a single task from the list and removing multiple tasks from the list. As you can see, there are two comments in the code that describe that, and we will go into more detail ourselves.
Firstly, the app will check whether what is in the completeTask variable is a string or not. If it is a string, then there is only one task in the variable. Therefore, only one task is to be removed. Then, the app proceeds with the removal with the next line: it adds the task to an array called complete. Then, after the second comment, the app will look for the index of completeTask in the task array and remove it via the splice method, which effectively removes the task from the primary task list.
However, if completeTask is not a string (single item) but an object (multiple items), then it moves on to the next part of the code that begins with else if. If the app finds out multiple items are to be removed from the list, it will ignore the first check and skip to this part instead. The process is similar to what we described above, but a loop is added, which checks the completedTask array to ensure that each task is pushed to the complete array. Finally, for each task in the loop, it finds the index in the task array and removes it using the splice method again.
In the end, the app is instructed to redirect the user’s browser back to the root, which refreshes the page and shows the new task list.
app.post("/removetask", function(req, res) {
var completeTask = req.body.check;
//check for the “typeof” the different completed task, then add to the complete task
if (typeof completeTask === "string") {
complete.push(completeTask);
//check if the completed task already exists in the task when checked, then remove it
task.splice(task.indexOf(completeTask), 1);
} else if (typeof completeTask === "object") {
for (var i = 0; i < completeTask.length; i++) {
complete.push(completeTask[i]);
task.splice(task.indexOf(completeTask[i]), 1);
}
}
res.redirect("/");
});
And, finally, the last bit of code to complete the application. The two final blocks of code serve to render the page in your browser and also inform you that your Node.js application is listening to port 3000. The first line after the comment sets up a GET route using the app object. It listens for incoming HTTP GET requests on the root URL: the URL you are using for your application, be it http://localhost:3000 or a domain name.
When the app gets such a request, it renders the HTML page we defined earlier via EJS in the index.ejs file. Then, it also sends data to the template via two objects with the task and complete properties. These properties are populated by their respective arrays and will display the task and completed list on the page.
Then, the app begins listening on port 3000 and informs you about it.
//render the ejs and display added task, completed task
app.get("/", function(req, res) {
res.render("index", { task: task, complete: complete });
});
//set app to listen on port 3000
app.listen(3000, function() {
console.log("server is running on port 3000");
});
With all of that out of the way, we are almost done! All that is left is to open your terminal or command line, navigate to the directory for the application, and simply start it. You can use the node index.js and pm2 start index.js commands. That way, Node will start your application, and then PM2 will keep it running even after closing the terminal. When it starts, the application will look like this.
The complete code for each of the three files we created can be found below for your convenience.
//dependencies required for the app
var express = require("express");
var bodyParser = require("body-parser");
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.set("view engine", "ejs");
//render css files
app.use(express.static("public"));
//placeholders for added task
var task = ["buy socks", "practise with nodejs"];
//placeholders for removed task
var complete = ["finish jquery"];
//post route for adding new task
app.post("/addtask", function(req, res) {
var newTask = req.body.newtask;
//add the new task from the post route
task.push(newTask);
res.redirect("/");
});
app.post("/removetask", function(req, res) {
var completeTask = req.body.check;
//check for the "typeof" the different completed task, then add into the complete task
if (typeof completeTask === "string") {
complete.push(completeTask);
//check if the completed task already exits in the task when checked, then remove it
task.splice(task.indexOf(completeTask), 1);
} else if (typeof completeTask === "object") {
for (var i = 0; i < completeTask.length; i++) {
complete.push(completeTask[i]);
task.splice(task.indexOf(completeTask[i]), 1);
}
}
res.redirect("/");
});
//render the ejs and display added task, completed task
app.get("/", function(req, res) {
res.render("index", { task: task, complete: complete });
});
//set app to listen on port 3000
app.listen(3000, function() {
console.log("server is running on port 3000");
});
<html>
<head>
<title> ToDo App </title>
<link href="https://fonts.googleapis.com/css?family=Lato:100" rel="stylesheet">
<link href="/styles.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h2> A Simple To-do List App </h2>
<form action ="/addtask" method="POST">
<input type="text" name="newtask" placeholder="Add New Task">
<button> Add Task </button>
<h2> Added Task </h2>
<% for( var i = 0; i < task.length; i++){ %>
<li><input type="checkbox" name="check" value="<%= task[i] %>" /> <%= task[i] %> </li>
<% } %>
<button formaction="/removetask" type="submit" id="top"> Remove </button>
</form>
<h2> Completed task </h2>
<% for(var i = 0; i < complete.length; i++){ %>
<li><input type="checkbox" checked><%= complete[i] %> </li>
<% } %>
</div>
</body>
</html>
* {
font-family: "optima", sans-serif;
font-weight: bold;
color: rgb(0, 0, 0);
margin-top: 10px;
}
body {
background: rgb(169, 169, 169);
color: #333333;
margin-top: 50px;
}
.container {
display: block;
width: 300px;
margin: 0 auto;
border: 3px solid black;
padding: 15px;
}
ul {
margin: 0;
padding: 0;
}