SW design week SW design week
This week up to 80% off on software design courses. More info

Lesson 5 - Creating an OOP Diary In JavaScript

In the previous lesson, Reference And Value Data Types In JavaScript, we explained the differences between value and reference data types. We already know that when we store a class instance to a variable, it actually contains a reference to that instance. So we can use a single instance from several variables or simply pass it somewhere without being copied.

As promised, in today's object-oriented programming tutorial for JavaScript, we're going to start programming an electronic diary. We'll also use the knowledge from the previous lesson.

Preparation

But first let's think about what we're going to need. We'll create a simple page, index.html, with a form to add an entry at the top and an entry list below. We'll insert two inputs into some container, e.g. into the <div> element. They will be of the text and date types for the task name and its date. Finally, we'll add a confirmation button. Below, we'll add a second <div> for the task list.

As for JavaScript, we can create the js/ folder and three scripts: Diary.js, Entry.js, and app.js. We'll also link them in our page.

Therefore, the index.html file could look like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Diary</title>
</head>
<body>
    <h1>Diary</h1>
    <div>
        <input type="text" id="name" placeholder="Fill in the task name"><br>
        <input type="date" id="date" placeholder="Fill in the date"><br>
        <button id="confirm">Save task</button>
    </div>
    <div id="task-list">

    </div>
    <script src="js/Diary.js"></script>
    <script src="js/Entry.js"></script>
    <script src="js/app.js"></script>
</body>
</html>

The page might look like this:

Your page
localhost

We won't deal too much with the design for now.

Entry

Let's start with the Entry.js file. As you might guess, the file will contain a class representing one entry in our diary. Therefore, we can give it different properties that we can change when creating or editing the entry. So far it can be a name, a date, and whether the task has been completed. We'll set the first 2 properties by the constructor and as far as the completion of the task is concerned, we'll assume that it's always undone by default:

class Entry {

    constructor(name, date) {
        this.name = name;
        this.date = date;
        this.done = false;
    }

}

Diary

Now let's move to the diary itself.

Constructor

Let's create a Diary class with a constructor in the Diary.js file and declare several properties in it:

  • entries - We'll create the diary entries as an empty array.
  • language - The language to format the date for may be useful in the future, as different languages have different formats. E.g. English dates look different than European. To set the language, we'll add a parameter to the constructor. Since we'll mostly want the English environment, we'll define it with the default value of en-US, which will be used if we don't specify the parameter.

The class might look like this:

class Diary {

    constructor(language = "en-US") {
        this.entries = [];
        this.language = language;
    }

}

Selecting Page Elements

In the class, we're going to need to work with DOM elements on the page. You may have already encountered the principle of separating the application logic from the user interface. This basic programming rule is used e.g. by the MVC architecture.

In JavaScript frameworks that you'll get to after finishing this course, the application architecture is designed so that the code selecting elements on the page does not mix with another application code. It would be very confusing otherwise.

We won't create any complicated architecture in this basic OOP course, but we'll try to place all selections of elements on the page at one place in the class. This place will be the constructor. We'll create a few more properties there to store elements from the page that we're going to need further in the class:

  • nameInput - The input element for the name of the entry being added
  • dateInput - The input element for the date of the entry being added
  • confirmButton - The save button
  • printElement - The element for listing the entries stored in the diary

The constructor will now look like this:

constructor(language = "en-US") {
    this.entries = [];
    this.language = language;

    this.nameInput = document.getElementById("name");
    this.dateInput = document.getElementById("date");
    this.confirmButton = document.getElementById("confirm");
    this.printElement = document.getElementById("task-list");
}

We'll not select elements anywhere else in the class, because it'd be very unreadable.

Methods

Let's move to the diary's methods.

setEvents()

In order for our constructor not to be too long, we'll set the event handlers of the page elements in a separate method. In our case, it's just a click on the button.

So far, let's add a naive implementation of the button-handling method that won't work. We'll explain why in a moment:

setEvents() {
    this.confirmButton.onclick = function() { // this code won't work
        const entry = new Entry(this.nameInput.value, this.dateInput.value);
        this.entries.push(entry);
        this.printEntries();
    };
}

The method wires a handler function to the onclick event of the this.confirmButton button. That's still all fine. Inside, the values from the inputs are taken and a new entry is created based on them. We insert this new instance into the array. All the entries are then listed.

So what's wrong? If you paid attention in the JavaScript Basic Constructs, you know that:

Using function to handle element events changes the context and the this keyword then references the element that caused the event. Therefore, this does not contain the instance of our class anymore. This behavior is a flaw in JavaScript's design and prevents us from working with instance variables and methods from within event handlers.

Arrow functions

There are several workarounds. We'll mention the simplest solution. We'll use an arrow function to handle the event, which is a "shorter" syntax to declare functions. The name is based on the arrow sign we use to declare these. Arrow functions were added to JavaScript later, so they no longer suffer from the context change error. More precisely, they have no context, and the this keyword contains what was there before, unchanged.

To store an arrow function to a variable, we use the following syntax:

functionName = () => {
    // function body
}

If we wanted to pass parameters to the function, we write them in parentheses as we are used to:

functionName = (parameter) => {
    // function body
}

If we want to pass one parameter only, we can even omit the parentheses:

functionName = parameter => {
    // function body
}

Now, we'll fix the setEvents() method to make the this keyword work in the handler function. We need the this keyword to access our class' properties:

setEvents() {
    this.confirmButton.onclick = () => { // this now stays this
        const entry = new Entry(this.nameInput.value, this.dateInput.value);
        this.entries.push(entry);
        this.printEntries();
    };
}

We'll call the method at the end of the constructor:

this.setEvents();

printEntries()

Nothing will surprise us in the method for listing tasks, it works very much like the list of employees we created in previous lessons:

printEntries() {
    this.printElement.innerHTML = "";
    for (let i = 0; i < this.entries.length; i++) {
        const entry = this.entries[i];
        this.printElement.innerHTML += `<h3>${entry.name}</h3>when: ${entry.date}<br>done: ${entry.done}`;
    }
}

The method deletes all the content from our element and lists our tasks one by one using a loop.

App.js

Finally, we still need to create a Diary instance in app.js and list the saved entries. They aren't there yet at the moment of creating the diary, but we'll retrieve them from the local storage further in the course:

const diary = new Diary();
diary.printEntries();

If we now run our app in the browser, it'll look like this:

Your page
localhost

In case of any problems, compare your code with the working solution in the attachment.

In the next lesson, Objects, JSON, And Enhancing the JavaScript Diary, we'll think about storing entries so they won't get lost after refreshing or closing the page. We'll talk about the popular JSON format. And we'll also group the tasks on the same day and make the listing of the entries nicer.


 

 

Activities (2)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!