Learn HTML & CSS Learn HTML & CSS
Only this week 80 % discount on HTML & CSS and JavaScript e-learning courses!

Lesson 7 - Improving the Object-Oriented Diary In JavaScript

In the previous lesson, Objects, JSON, And Enhancing the JavaScript Diary, we improved our diary and added saving entries to localStorage. Today, we're going to add sorting by date, grouping entries with the same date, and deleting entries.

Listing Entries

First, we'll edit the listing of the entries. It'd be best to sort the entries by date and group the entries happening on the same day, which we'll list below each other in one block.

Sorting

First, we'll implement a method for sorting the entries. To do this, we'll use the sort() method on the array. It sorts the array by comparing element pairs. As a parameter, it accepts a comparison function that defines how to compare 2 elements from the array. Our method in the Diary class to sort the entries array will be as follows:

sortEntries() {
    this.entries.sort(function (entry1, entry2) {
        return (new Date(entry1.date) - new Date(entry2.date));
    });
}

The function compares the dates of two entries that we parse to dates using the Date object's constructor. Of course, we pass the date property from the entries. To compare two dates, we simply use the - operator which returns their difference in milliseconds. If the first date is after the second, a negative number is returned. If they are the same, 0 is returned. Otherwise, a positive number is returned. It's a positive, negative, or zero number what the sort() method needs to determine whether the date is greater, smaller, or equal.

We'll call the method every time before printing the entries:

printEntries() {
    this.sortEntries();
    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}`;
    }
}

We could also call the sorting after adding an entry and after loading them so that we won't have to call it each time we list them. The disadvantage of this solution, however, would be that we could forget about it while manipulating the entries elsewhere.

If we now run the diary and have some data in localStorage, these will be ordered by date.

Grouping

Finally, we're going to finish listing the entries. We'll print the date and all the entries occurring on that day. We can easily modify our loop:

printEntries() {
    this.sortEntries();
    this.printElement.innerHTML = "";
    let lastDate = null;
    for (const entry of this.entries) {
        if (entry.date !== lastDate) {
            this.printElement.innerHTML += `<h3>${entry.date}</h3>`
        }
        lastDate = entry.date;

        this.printElement.innerHTML += `<strong>${entry.name}</strong><br>done: ${entry.done}<hr>`;
    }
}

We assign the date property from the previous entry to the lastDate variable. Since there's no last entry when the loop's running for the first time, we initialize the variable to null for the start. We then print the date of the current entry only if it differs from the last one. So the entries on the same day will be grouped. Now we have the tasks occurring on the same date printed nicely under each other and sorted:

Your page
localhost

Formatting the date And done Properties

Since the date format and the way we print the done property are also not ideal, we need to edit the printing to make it more "human-friendly" :) Remember the language property that we can set in the constructor? Now we're going to use it to print the date better and at the same time we'll improve printing of the done property:

printEntries() {
    this.sortEntries();
    this.printElement.innerHTML = "";
    let lastDate = null;
    for (const entry of this.entries) {
        if (entry.date !== lastDate) {
            const date = new Date(entry.date).toLocaleDateString(this.language, {
                weekday: "long",
                day: "numeric",
                month: "short",
                year: "numeric"
            });
            this.printElement.innerHTML += `<h3>${date}</h3>`
        }
        lastDate = entry.date;

        this.printElement.innerHTML += `<strong>${entry.name}</strong><br>task ${!entry.done ? "not " : ""}done<hr>`;
    }
}

We create an instance of the Date object based on our date and use its toLocaleString() method, to which we pass the language property of our class and as the second parameter a formatting object whose properties specify how exactly the date should be printed.

We use a simple ternary operator to print the done property, we add either "not " or an empty string.

The result now looks like this:

Your page
localhost

Deleting Entries

We'll continue with the basic interaction with our tasks, delete them, or mark them as done. Let's start with deleting.

Saving Entries

Since we'll need to save the entries again after deleting, let's move saving the entries from the setEvents() method to a separate saveEntries() method:

saveEntries() {
    localStorage.setItem("entries", JSON.stringify(this.entries));
}

We'll now call the saveEntries() method in the setEvents() method:

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

The Button

We'll generate a button for each entry to remove it. We'll create it as a new <button> element using the document.createElement() method and put it into a <div> using appendChild(). We'll also add a click event handler to the button, removing the entry from the array, saving the entries to localStorage, and returning them. The printEntries() method after adding a delete button will look like this:

printEntries() {
    this.sortEntries();
    this.printElement.innerHTML = "";
    let lastDate = null;
    for (const entry of this.entries) {
        if (entry.date !== lastDate) {
            const date = new Date(entry.date).toLocaleDateString(this.language, {
                weekday: "long",
                day: "numeric",
                month: "short",
                year: "numeric"
            });
            this.printElement.innerHTML += `<h3>${date}</h3>`
        }
        lastDate = entry.date;

        this.printElement.innerHTML += `<strong>${entry.name}</strong>
        <br>task ${!entry.done ? "not " : ""}done`;
        const deleteButton = document.createElement("button");
        deleteButton.onclick = () => {
            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();
            }
        };
        deleteButton.innerText = "Delete entry";
        this.printElement.appendChild(deleteButton);
        this.printElement.innerHtml += "<br>";
    }
}

Note the use of the confirm() confirmation dialog, removing the record is certainly an action we don't want to make by mistake :)

You might want to insert the button directly into the HTML code as text and assign it the array index of the entry to delete as a data attribute. Such buttons would then all be selected and handled to delete an element from the array below the given index. However, the problem would arise if we opened the diary in multiple browser tabs at once. If we deleted an item in one tab and did not refresh the other tab, that item would still be there, but in localStorage, there would be a totally different item under that index. Like that, we could unintentionally delete some other task in a non-refreshed tab. Therefore, we'll always do all of the item manipulations directly using anonymous functions to which we will pass this one particular item.

If you shake your head over the item removal code:

this.entries = this.entries.filter(e => e !== entry);

Unfortunately, this is currently the easiest way to delete an element from an array in JavaScript if we don't know it's index and don't wont to bother looking for it either. The code filters the array so that only entries that don't match the entry that we want to remove remain in it.

We'll continue next time, in the lesson Finishing an Object-Oriented Diary In JavaScript, when we'll add a button to make a given task done and complete the diary by adding simple CSS styles :)


 

 

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!