Simple JS Projects: Project 3: MyBookList App

A simple responsive BookList app built using vanilla.js and Bootswatch. It uses persistent local storage to store book details.

The project that we will build uses only vanilla.js and no frontend frameworks. It uses ES6 Classes and Objects syntax.

Designing the HomePage

It a simple page that contains a header, a form with 4 input fields namely Title, Author, ISBN and an Add Book with the type of submit and a table with 4 columns to store the title, author, ISBN and a delete button to remove books which appears once we add our first book respectively.

Adding functionality with JS

Let’s structure our code. We will divide our logic into 3 separate classes. They are

Book Class: To represent a book

UI Class: To handle UI tasks

Store Class: To handles storage

Whenever we add a book, an object of the Book class is initiated.

Code for Book Class

class Book {
  constructor(title, author, isbn) {
    this.title = title;
    this.author = author;
    this.isbn = isbn;
  }
}

Our UI class consists of 5 static functions. They are:

displayBooks()- To display books. We first look for books in the local storage if books are there we retrieve those books and for each book, we need to call a static method addBookToList of the same UI class.

addBookToList()- We create an HTML element and add book details such as title, author, ISBN and delete using innerHTML and append it to the parent element.

deleteBook()- To delete a book we look for an element that contains deleted class and then we go to the parent element and call remove().

showAlert()- To show alert messages

clearFields()- To clear all our input field so that we can enter another book details.

Code for UI Class

// UI Class: Handle UI Tasks
class UI {
  static displayBooks() {
    const books = Store.getBooks();

    books.forEach((book) => UI.addBookToList(book));
  }
  static addBookToList(book) {
    const list = document.querySelector("#book-list");
    const row = document.createElement("tr");

    row.innerHTML = `
        <td>${book.title}</td>
        <td>${book.author}</td>
        <td>${book.isbn}</td>
        <td><a href="#" class="btn btn-danger btn-sm deleted">X</a></td>
        `;

    list.appendChild(row);
  }

  static deleteBook(el) {
    console.log(el);

    if (el.classList.contains("deleted")) {
      el.parentElement.parentElement.remove();
    }
  }

  static showAlert(message, className) {
    const div = document.createElement("div");
    div.className = `alert alert-${className}`;
    div.appendChild(document.createTextNode(message));
    const container = document.querySelector(".container");
    const form = document.querySelector("#book-form");
    container.insertBefore(div, form);

    // Remove alert
    setTimeout(() => document.querySelector(".alert").remove(), 3000);
  }
  static clearFields() {
    document.querySelector("#title").value = "";
    document.querySelector("#author").value = "";
    document.querySelector("#isbn").value = "";
  }
}

Our Store consists of 3 static functions.They are:

getBooks()- To get all the books that are stored in local storage. If there are no books we need to initialize an array or else we get the books using getItem and we need to parse the retrieved data. using JSON.parse .

addBook()- We first get all the book details from local storage and then we push it to the books array. Pushing the details of a new book in the array is not enough we need to store this updated array back to the local storage using setItem . To set these books array we need to stringify the array using JSON.stringify. You can read more about localStorage, JSON.stringify() and JSON.parse().

Code for Store Class

// We need to stringify before storing and parse it before retrieving it
class Store {
  static getBooks() {
    let books;
    if (localStorage.getItem("books") === null) {
      books = [];
    } else {
      books = JSON.parse(localStorage.getItem("books"));
    }
    return books;
  }
  static addBook(book) {
    const books = Store.getBooks();
    books.push(book);
    localStorage.setItem("books", JSON.stringify(books)); // books is an array of objects
  }
  static removeBook(isbn) {
    const books = Store.getBooks();
    books.forEach((book, index) => {
      if (book.isbn === isbn) {
        books.splice(index, 1);
      }
    });
    localStorage.setItem("books", JSON.stringify(books));
  }
}

I have used 3 event listener:

DOMContentLoaded - To display books when the initial HTML document has been fully loaded and parsed.

submit - To add a book when we click Add Book . We need to validate the form so that when the user don’t fill all the book details we display an alert message or else we initiate a book object, add it to the UI Class, store the book details to local storage using Store class, Show the success alert and clear the input fields.

click- To remove a book when we click the delete button. We first need to delete the book from the UI class deleteBook()and then remove the book from the Local Storage using removeBook() of Store class. Finally we display a success message that the book was deleted.

Code for Event Listeners

// Event: Display Books
document.addEventListener("DOMContentLoaded", UI.displayBooks);
// Event: Add a Book
document.querySelector("#book-form").addEventListener("submit", (e) => {
  // Prevent actual submit
  e.preventDefault();

  // Get form values
  const title = document.querySelector("#title").value;
  const author = document.querySelector("#author").value;
  const isbn = document.querySelector("#isbn").value;

  // Validate
  if (title === "" || author === "" || isbn == "") {
    UI.showAlert("Please fill in all fields", "danger");
  } else {
    // Instantiate book
    const book = new Book(title, author, isbn);

    // Add Book to UI
    UI.addBookToList(book);

    // Add Book to Store
    Store.addBook(book);

    // Show success message
    UI.showAlert("Book Added", "success");

    // Clear Fields
    UI.clearFields();
  }
});

// Event: Remove a Book
document.querySelector("#book-list").addEventListener("click", (e) => {
  // Remove Book from UI
  UI.deleteBook(e.target);

  // Remove Book from Store
  Store.removeBook(e.target.parentElement.previousElementSibling.textContent);

  // Show delete message
  UI.showAlert("Book Removed", "success");
});

Finished Project

bookList

Hope you have enjoyed this article! Happy Coding

blog