Lesson 9 - Finishing an Object-Oriented Diary In JavaScript
In the previous lesson, Improving the Object-Oriented Diary In JavaScript, we added a few more features to our object-oriented diary in JavaScript. Today, we're going to continue in a similar way and complete the application.
Changing the done Property Of the Entries
The last button we're missing is the button to accomplish the task.
Parametrization
Because the button to make the task done will look and work very much like the delete button, we won't add duplicate code to our application unnecessarily, but we'll rather modify the existing one. Let's create a helper method to insert a task button:
_addButton(title, callback) { const button = document.createElement("button"); button.onclick = callback; button.innerText = title; this.printElement.appendChild(button); }
The _addButton()
method does exactly what we did to add the
delete button. However, the button's title and handler method are set from a
parameter, so we can use the method to insert different buttons with different
titles and handlers.
We can replace all the code for the remove button in the
printEntries()
method with the following one:
this._addButton("Delete", () => { if (confirm("Are you sure you want to remove the task?")) { this.entries = this.entries.filter(e => e !== entry); // Keep everything not equal to the entry variable this.saveEntries(); this.printEntries(); } });
We created the helper method intentionally now and not before adding the first button. It was to show how to handle a situation when you need to put some very similar code to your application. You move the existing code to an auxiliary method and parameterize it. Redundant code violates the DRY principle, one of the most important rules for high-quality object design.
Private Methods
Note that the method name starts with an underscore. Auxiliary methods that no one should call on our class from the outside should be ideally hidden. Unfortunately, JavaScript currently doesn't allow to encapsulate methods easily. In the course, we'll show that it's possible, but only using hacks, which will make the code confusing. Marking private properties and methods with an underscore is a quite good habit and it's used in other languages that don't support access modifiers as well.
In order for our code to be really well written, we'll mark the
_setEvents()
method with an underscore as well. It's also just an
auxiliary functionality that shouldn't be called from the outside.
_setEvents() { // ... }
Remember to rename the method call in the constructor:
constructor(language = "en-US") { // ... this._setEvents(); }
The Button To Make the Entry Done
After a small detour, let's go back to the new button. We'll add it using the
prepared _addButton()
method:
this._addButton("Mark as " + (entry.done ? "done" : "not done"), () => { entry.done = !entry.done; this.saveEntries(); this.printEntries(); });
You can try the result
Sanitizing User Inputs
So we have a fully-functional diary using localStorage
. All we
are missing is to sanitize user inputs, because if the user doesn't enter a
date, it will be listed as "Invalid Date". So we'll modify the
_setEvents()
method and make a simple validation there. Because
<input type ="date">
returns an empty string
as
the value
if the date is not provided, it's not difficult to alert
the user about this problem:
_setEvents() { this.confirmButton.onclick = () => { // this now stays this if (this.dateInput.value !== "") { const entry = new Entry(this.nameInput.value, this.dateInput.value); this.entries.push(entry); this.saveEntries(); this.printEntries(); } else alert("You must fill in the date!"); }; }
So, if the date isn't entered correctly, we notify the user using the
alert()
function to enter it correctly, otherwise we save the new
entry.
Design
Now there's just some design left, which we can go through quickly while
editing the JS only a little. We'll create a css/
folder and a new
style.css
file in the project. It's contents will be as
follows:
* { font-family: Segoe UI Light, "Calibri Light", sans-serif; } button { padding: 10px 16px; font-weight: 600; background: #2792e0; color: #fff; border: none; cursor: pointer; transition: .5s; } button:hover { background: #2954c2; } button:active { background: #204199; } input { min-width: 250px; padding: 8px; border: 1px solid #909090; height: 20px; } button, input { margin-top: 1rem; outline: none; } .task { width: 75%; padding: 2rem; margin: 1rem auto; border: 1px solid #c5c5c5; box-shadow: 2px 2px 4px #b1b1b1; }
We'll link the style in index.html
:
<link href="css/style.css" rel="stylesheet">
And now we can easily adjust how each entry is printed:
this.printElement.innerHTML += `<h4>${entry.name}</h4> <br>task ${!entry.done ? "not " : ""}done`; // ... this.printElement.innerHtml = `<div class="task">` + this.printElement.innerHtml + `</div>`;
The result:
And it's done! So we have a
pretty looking diary that stores data and can be used in real life
If you havd any problem with
anything, you can download the complete source code below and find your
error.
Next time, in the lesson Inheritance And Polymorphism In JavaScript, we'll move further and look at inheritance and polymorphism.