project / February 3rd, 2021
The to-do list is a web app that will show the user a checklist, with the options to add new items to the list and delete items off the list. By default, this app will display a list with the current date as the list name. The user can then use the search bar to enter a new list title to create a new list, or enter an existing list name to load an existing list.
The to-do list also serves as the main project for the EJS module from the Complete Web Development 2021 Bootcamp.
Throughout the process of building the to-do list, I learned about EJS templates and partials in order to build the foundation - or version 1 of this app. Using EJS templates allows us to change certain parts of a page depending on the logic in our server and avoid the repetition of having to create separate HTML files for each list. A fundamental lesson of templating that was learned through this project is passing data from the template to the back-end server and vice versa, in order to populate the to-do list dynamically. Additionally, the concepts of Node module exports and scope in JavaScript were also covered.
In version 2, this app is taken to the next level by connecting the to-do list with a MongoDB database - first on a local server and then on the MongoDB Atlas cloud service. Methods from the Mongoose library are then applied to the database to create, read, update, and delete items and lists. Express route parameters for dynamic routing and the Lodash library are also used to build this version of the app.
EJS (Embedded JavaScript)
Node.js
Express.js
MongoDB databases and Mongoose
Scope of local and global variables in JavaScript
Node Module Exports
Lodash
MongoDB Atlas
Heroku
The bootcamp has provided a starting file for the stylesheet, allowing my focus to be solely placed on creating the functionality of the to-do list with EJS templates, transferring data from the template to the server and vice versa using Express.js, and MongoDB database operations.
One major modification I made to the original project was adding a search bar underneath the date at the top of the checklist, in order to allow the user to create new lists or switch to an existing custom list.
After installing EJS using Node Package Manager, the app's view engine is set to EJS. This means that during runtime, the engine will replace variables in an EJS template file with actual values and transform the template into a page that is rendered for the user.
The next step is to create an EJS template named list, which serves as the base template to create all the lists in the app. The goal is to then execute logic from the back-end to grab the current date and pass it as a variable to the list template file, which will be displayed as an HTML header through the use of EJS markers.
Once successful, the next step is to build the functionality of the to-do list. The structure of the to-do list is initially formed using an HTML unordered list that is populated with list items. To add items to the list, the new item name is submitted from a form to the server using a POST request. The new item name is then passed from the server back to the list template as a variable that will be rendered as a list item inside the unordered list. Finally, the page and updated to-do list is rendered again by redirecting to the home route.
However, a problem arises whenever a subsequent POST request is made. The previous value of the variable used to pass in the new item is overwritten. Consequently, a new item added would replace the previous new item, when it should be added below the previous one. Essentially, the code at this point allows only one piece of data to be stored in the variable, with all older values being overwritten and erased from the list.
To solve this, all the list items must be stored in an array. Newly added items are appended to the array and the array is passed from the server to the list template. In the template, the EJS scriptlet tag is used to enact a FOR loop through the array and render each array item as a list item inside the unordered list.
A stylesheet provided with the bootcamp course resources is then used and modified with my own personal touch to apply the styles seen in the above screenshot. Additionally, the list template is adjusted so that each item is presented as a checkbox instead of the default list bullet point. A submit button for adding new items is added next to the form at the bottom of the list.
For scenarios in which the user wishes to visit a page that is structurally different from a list (eg. an about or contact page), we use partials. Partials involves writing a header and footer as separate EJS files, then importing them to every page template in the web app. This is useful for avoiding repetitive tasks like creating an about or contact HTML page and having to write the same header and footer in each page. More attention can be paid to the content between the header and footer that is different for each page. Styles can also be imported in the header, so that every page with the header is styled. Ultimately, I scrapped plans to add an about and contact page because I didn't feel it was necessary for this app.
In order to have a deeper understanding how Node modules like EJS and Express work, a date.js module was created from scratch. Because the code written to generate the current date doesn't belong in our main server logic, it instead becomes a separate local module that is reusable anywhere and can be required in the main server file to run the code inside it. This is accomplished through using the exports property for Node modules and setting it equal to the function used to get the current date.
An issue with the initial version of this app created in Part 1 is that the added items disappear from the list whenever the server is restarted. This is because the items are stored in an array and when the server restarts, the array is wiped down to the default items it starts with. After learning about databases and how to persist data, this knowledge is applied to update the to-do list app to use MongoDB.
Mongoose is installed and used to create a database inside MongoDB, and then the items collection is created in this database. Through the mongoose find method, the documents in the items collection are read and sent to the list template to be rendered in the to-do list. Additionally, if the items collection is empty, then it is automatically populated with default list items.
To add new items, the Mongoose save method is used to save a new item into the items collection. To delete an item when a checkbox is checked, each item must be a form that triggers a POST request to a delete route when clicked. This is accomplished using the onChange attribute for the checkbox, which will trigger a line of JavaScript code and submit the ID of the item that was checked. The findByIdAndRemove Mongoose method is then used to remove the item from the items collection.
In order to have multiple lists and dynamically create them, Express route parameters are used in order to create dynamic routes. Whenever a GET request is made to a new custom route, it will create a new list which is a new document that is saved to a new database collection called lists. Each list document will have a name that is equal to the title of the custom route and an array of item documents associated with it, in order to form the list.
*ASIDE: Although not a part of the course lessons, I also added a search bar to allow the user to dynamically create a new list using dynamic routing or switch over to an existing list. The reason being is that the only method to access another list at this point was to add the route name to the URL in the browser bar, is which not user-friendly.
If the user has already created a list with an existing name and simply wants access to it, the Mongoose findOne method is used to find the document with that existing name in the lists collection. The array associated with this document is then rendered as a list using the list template.
When a user tries to add a new item to a custom list, the form will still make a POST request to the root route like the default list would. To account for custom lists, an IF statement is used to check if the list that the user submitted an item from was from the default list (where the title is equal to the current date). Otherwise, that means the item was submitted from a custom list. In that case, findOne is used to find the custom list in the lists document and add the new item to its items array. The user is then redirected back to the custom list. Otherwise, the item is simply saved in the items collection and the user is redirected back to the root route.
When a user deletes an item from a custom list, we want to ensure the item is deleted from the correct list. Given that there is no submit button for each checkbox to pass in the current list name, it cannot be determined which list the deleted item came from. This is remedied through using the hidden input type, which can pass in the list name to the server when a form is submitted. The list name is then checked to determine if the user is deleting an item from the default list or a custom list. If the user is deleting an item from a custom list, that list document has to be located first before pulling an item from its array of list items. This is done using the Mongoose findOneAndUpdate method. Otherwise, the item is deleted directly from the items collection using Mongoose findByIdAndRemove.
Finally, the Lodash library is used to remove the differentiation between capitalized and all-lower case route names (eg. /Home and /home). This ensures that regardless of which version is entered in the search bar, the route and list title will always be capitalized.
Upon completion, a server is set up on the MongoDB Atlas cloud service in order to host the to-do list database on the web 24/7. The back-end of the app will liaise with MongoDB Atlas to access data from the database and users will be able to use the to-do list on the web. There is no longer any need to host the database locally!
This project was an excellent experience in gaining a broader grasp of the full-stack web development process, as it presented opportunities to touch on many different aspects like building a front-end with EJS templates, a back-end with MongoDB, and learning how the two parts are connected. Personally, this course project marked the first time I felt had built something tangible and useful because I was starting to seek ways to deviate from the original project and improve it, which resulted in the addition of the search bar to access or create custom lists.
Currently, the to-do list is meant for "public" use, as the lists can be accessed by anyone. To change this, a collection of users would have to be added to the database, along with authentication and security so that users can log into their own account and have access to their own lists. If this idea is ever implemented, it would also be useful to add a sidebar to display the lists that have been created by the user and also provide easy access to them as links.
Another potential minor improvement is adding a navigation bar with links to pages like home, contact, or a FAQ in order to provide help for users and improve the user experience. For now, I ultimately chose to not implement this due to time-constraints and a desire to complete other projects in timely fashion.