Reputation: 498
Scenario: Just created a simple book List app, which can accept some inputs and display it on the UI and has other functionalities such as the use of localStorage.
Just as I added more custom alerts (e.g. show alert - on adding a book, - if author name contain numbers etc.) The whole alert stuff has gone AWOL. (Previously defined alerts worked fine before)
Also none of the questions that showed similarity proved useful to me
Method which handles all the alerts in the page
static showAlert(msg, className) {
const alertBox = document.createElement('div');
alertBox.className = `alert alert-${className} mb-3 p-2`;
alertBox.appendChild(document.createTextNode(msg));
const container = document.querySelector('.container');
const form = document.querySelector('#book-form');
// check whether if an alertBox already exist in the UI
if(document.querySelector('.alert')) {
// remove alertBox after some time
setTimeout(() => {
document.querySelector('.alert').remove();
}, 3000);
} else {
container.insertBefore(alertBox, form);
}
}
And this is where I added new custom alerts:
if(bookName === '' || authorName === '' || bookId === '') {
Dom.showAlert('All fields are required', 'danger');
} else if (!isNaN(bookName)) {
Dom.showAlert('Name of the book should not contain numbers', 'danger');
document.querySelector('#title').value = '';
} else if (!isNaN(authorName)) {
Dom.showAlert('Author Name should not contain numbers', 'danger');
document.querySelector('#author').value = '';
} else if (isNaN(bookId)) {
Dom.showAlert('Reference Id should only consist of numbers', 'danger');
document.querySelector('#bookId').value = '';
Problems facing:
Quick note: I have set the setTimeout
delay to 3000
Existing alert is not removed after 3 seconds (which worked fine before)
If there is a need for another alert to exist in dom, then the alert showed previously should be removed and the new alert should be displayed; even if that happens in under 3 seconds.
I have added the code as snippet as I had some trouble setting JSFiddle
// Book class
class Book {
constructor(title, author, id) {
this.bookName = title;
this.authorName = author;
this.bookId = id;
}
}
// Dom class : handle user interface / dom
class Dom {
static displayBooks() {
//dummy data to mimic data in localStorage
const savedBooks = [
{
bookName: 'Vikings',
authorName: 'Michael Hirst',
bookId: 9764363,
},
{
bookName: 'The Soldier',
authorName: 'David Gary',
bookId: 5764363,
},
]
//const books = Store.getBooks();
const books = savedBooks;
books.forEach((book) => Dom.addBookToList(book))
}
static addBookToList(bookInfo) {
const tbody = document.querySelector('#book-list')
const bookRow = document.createElement('tr');
bookRow.innerHTML =
`
<tr>
<td>${bookInfo.bookName}</td>
<td>${bookInfo.authorName}</td>
<td>${bookInfo.bookId}</td>
<td><a href="#" class="btn btn-danger btn-sm deleteBtn">Delete</a></td>
</tr>
`
tbody.appendChild(bookRow);
}
static deleteBook(target) {
if(target.classList.contains('deleteBtn')) {
target.parentElement.parentElement.remove()
// show alert if user deletes a book
Dom.showAlert('Book deleted successfully', 'warning');
}
}
static showAlert(msg, className) {
const alertBox = document.createElement('div');
alertBox.className = `alert alert-${className} mb-3 p-2`;
alertBox.appendChild(document.createTextNode(msg));
const container = document.querySelector('.container');
const form = document.querySelector('#book-form');
// check whether if an alertBox already exist in the UI
if(document.querySelector('.alert')) {
// remove alerBox after an interval
setTimeout(() => {
document.querySelector('.alert').remove();
}, 3000);
} else {
container.insertBefore(alertBox, form);
}
}
static clearFields() {
document.querySelector('#title').value = '';
document.querySelector('#author').value = '';
document.querySelector('#bookId').value = '';
}
}
// Store class - deals with localStorage
/*class Store {
static getBooks() {
let books;
if(localStorage.getItem('books') === null) {
// if there is no book in the localstorage, then we initialize an empty array (of objects);
books = [];
} else {
// since the data is in the format of string, we need to change it to a javascript object in order to to things with it.Therefore we use JSON.parse
books = JSON.parse(localStorage.getItem('books'));
}
return books;
}
static addBooks(book) {
const books = Store.getBooks();
books.push(book);
localStorage.setItem('books', JSON.stringify(books))
}
static removeBooks(bookId) {
const books = Store.getBooks();
books.forEach((book, index) => {
if(book.bookId === bookId) {
books.splice(index, 1);
}
});
localStorage.setItem('books', JSON.stringify(books));
}
}*/
// Events in Dom
document.addEventListener('DOMContentLoaded', Dom.displayBooks);
// Add new book to dom/ui
document.querySelector('#book-form').addEventListener('submit', event => {
event.preventDefault();
const bookName = document.querySelector('#title').value;
const authorName = document.querySelector('#author').value;
const bookId = document.querySelector('#bookId').value;
// input validation
if(bookName === '' || authorName === '' || bookId === '') {
Dom.showAlert('All fields are required', 'danger');
} else if (!isNaN(bookName)) {
Dom.showAlert('Name of the book should not contain numbers', 'danger');
document.querySelector('#title').value = '';
} else if (!isNaN(authorName)) {
Dom.showAlert('Author Name should not contain numbers', 'danger');
document.querySelector('#author').value = '';
} else if (isNaN(bookId)) {
Dom.showAlert('Reference Id should only consist of numbers', 'danger');
document.querySelector('#bookId').value = '';
} else {
// object instantiation of book
const book = new Book(bookName, authorName, bookId);
// Add submitted book to the list
Dom.addBookToList(book);
// Add Submitted bookk to the local Storage
//Store.addBooks(book);
// show alert after adding the book
Dom.showAlert('Book added successfully', 'success');
// Clear input fields after submitting
Dom.clearFields();
}
})
// Remove / Delete a book from the list
document.querySelector('#book-list')
.addEventListener('click', event => {
// Remove book from the Dom
Dom.deleteBook(event.target);
// Remove book from the local storage
//Store.removeBooks(event.target.parentElement.previousElementSibling.textContent);
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Book List app</title>
<!-- fontAwesome script -->
<script src="https://kit.fontawesome.com/39350fd9df.js"></script>
<!-- bootstrap css -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" type="text/css">
</head>
<body>
<div class="container mt-4">
<h1 class="display-4 text-center mb-4"><i class="fas fa-book text-success"></i> <span class="text-success">BlueStock</span> <span class="text-muted">Book Shop</span></h1>
<form action="#" id="book-form">
<div class="form-group mb-2">
<label for="title">Book Title</label>
<input type="text" name="title" id="title" class="form-control">
</div>
<div class="form-group mb-2">
<label for="author">Author Name</label>
<input type="text" name="author" id="author" class="form-control">
</div>
<div class="form-group">
<label for="book-id">Reference Id</label>
<input type="text" name="title" id="bookId" class="form-control">
</div>
<input type="submit" value="Add Book" class="btn btn-primary w-100 mt-3" id="submit">
</form>
<table class="table table-striped mt-4">
<thead>
<tr>
<th>Book Title</th>
<th>Author Name</th>
<th>Reference Id</th>
<th class="text-warning"><small>Delete book</small></th>
</tr>
</thead>
<tbody id="book-list"></tbody>
</table>
</div>
<script src="app.js" type="text/javascript"></script>
</body>
</html>
Upvotes: 0
Views: 697
Reputation: 17858
The issue I notice with your showAlert
:
showAlert(msg, className) {
// ...
if (document.querySelector('.alert')) {
/*
#1. At the first time this function called, this will always be null. Since '.alert' is not in the DOM yet (it wil be added to the DOM at the following 'else' block.
Therefore, setTimeout here won't be called, since the code never enters here at the first time.
#2. At the second time and forth, this will be called.
However, it will not enter the `else` block which is to add the alert into the DOM
*/
setTimeout(() => {
document.querySelector('.alert').remove();
}, 3000);
} else {
container.insertBefore(alertBox, form);
}
}
If we break it down your expected behavior, the code should probably somewhere like this;
static showAlert(msg, className) {
// If there's an existing alert, instantly remove it.
if (document.querySelector('.alert')) {
document.querySelector('.alert').remove();
}
// Create the alert
const alertBox = document.createElement('div');
alertBox.className = `alert alert-${className} mb-3 p-2`;
alertBox.appendChild(document.createTextNode(msg));
const container = document.querySelector('.container');
const form = document.querySelector('#book-form');
// Insert alert to the DOM.
container.insertBefore(alertBox, form);
// Remove the alert above after 3s
setTimeout(() => {
alertBox.remove();
}, 3e3);
}
Example final code as following;
// Book class
class Book {
constructor(title, author, id) {
this.bookName = title;
this.authorName = author;
this.bookId = id;
}
}
// Dom class : handle user interface / dom
class Dom {
static displayBooks() {
//dummy data to mimic data in localStorage
const savedBooks = [{
bookName: 'Vikings',
authorName: 'Michael Hirst',
bookId: 9764363,
},
{
bookName: 'The Soldier',
authorName: 'David Gary',
bookId: 5764363,
},
]
//const books = Store.getBooks();
const books = savedBooks;
books.forEach((book) => Dom.addBookToList(book))
}
static addBookToList(bookInfo) {
const tbody = document.querySelector('#book-list')
const bookRow = document.createElement('tr');
bookRow.innerHTML =
`
<tr>
<td>${bookInfo.bookName}</td>
<td>${bookInfo.authorName}</td>
<td>${bookInfo.bookId}</td>
<td><a href="#" class="btn btn-danger btn-sm deleteBtn">Delete</a></td>
</tr>
`
tbody.appendChild(bookRow);
}
static deleteBook(target) {
if (target.classList.contains('deleteBtn')) {
target.parentElement.parentElement.remove()
// show alert if user deletes a book
Dom.showAlert('Book deleted successfully', 'warning');
}
}
static showAlert(msg, className) {
// If there's an existing alert, instantly remove it.
if (document.querySelector('.alert')) {
document.querySelector('.alert').remove();
}
// Create the alert
const alertBox = document.createElement('div');
alertBox.className = `alert alert-${className} mb-3 p-2`;
alertBox.appendChild(document.createTextNode(msg));
const container = document.querySelector('.container');
const form = document.querySelector('#book-form');
// Insert alert to the DOM.
container.insertBefore(alertBox, form);
// Remove the alert above after 3s
setTimeout(() => {
alertBox.remove();
}, 3e3);
}
static clearFields() {
document.querySelector('#title').value = '';
document.querySelector('#author').value = '';
document.querySelector('#bookId').value = '';
}
}
// Store class - deals with localStorage
/*class Store {
static getBooks() {
let books;
if(localStorage.getItem('books') === null) {
// if there is no book in the localstorage, then we initialize an empty array (of objects);
books = [];
} else {
// since the data is in the format of string, we need to change it to a javascript object in order to to things with it.Therefore we use JSON.parse
books = JSON.parse(localStorage.getItem('books'));
}
return books;
}
static addBooks(book) {
const books = Store.getBooks();
books.push(book);
localStorage.setItem('books', JSON.stringify(books))
}
static removeBooks(bookId) {
const books = Store.getBooks();
books.forEach((book, index) => {
if(book.bookId === bookId) {
books.splice(index, 1);
}
});
localStorage.setItem('books', JSON.stringify(books));
}
}*/
// Events in Dom
document.addEventListener('DOMContentLoaded', Dom.displayBooks);
// Add new book to dom/ui
document.querySelector('#book-form').addEventListener('submit', event => {
event.preventDefault();
const bookName = document.querySelector('#title').value;
const authorName = document.querySelector('#author').value;
const bookId = document.querySelector('#bookId').value;
// input validation
if (bookName === '' || authorName === '' || bookId === '') {
Dom.showAlert('All fields are required', 'danger');
} else if (!isNaN(bookName)) {
Dom.showAlert('Name of the book should not contain numbers', 'danger');
document.querySelector('#title').value = '';
} else if (!isNaN(authorName)) {
Dom.showAlert('Author Name should not contain numbers', 'danger');
document.querySelector('#author').value = '';
} else if (isNaN(bookId)) {
Dom.showAlert('Reference Id should only consist of numbers', 'danger');
document.querySelector('#bookId').value = '';
} else {
// object instantiation of book
const book = new Book(bookName, authorName, bookId);
// Add submitted book to the list
Dom.addBookToList(book);
// Add Submitted bookk to the local Storage
//Store.addBooks(book);
// show alert after adding the book
Dom.showAlert('Book added successfully', 'success');
// Clear input fields after submitting
Dom.clearFields();
}
})
// Remove / Delete a book from the list
document.querySelector('#book-list')
.addEventListener('click', event => {
// Remove book from the Dom
Dom.deleteBook(event.target);
// Remove book from the local storage
//Store.removeBooks(event.target.parentElement.previousElementSibling.textContent);
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Book List app</title>
<!-- fontAwesome script -->
<script src="https://kit.fontawesome.com/39350fd9df.js"></script>
<!-- bootstrap css -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" type="text/css">
</head>
<body>
<div class="container mt-4">
<h1 class="display-4 text-center mb-4"><i class="fas fa-book text-success"></i> <span class="text-success">BlueStock</span> <span class="text-muted">Book Shop</span></h1>
<form action="#" id="book-form">
<div class="form-group mb-2">
<label for="title">Book Title</label>
<input type="text" name="title" id="title" class="form-control">
</div>
<div class="form-group mb-2">
<label for="author">Author Name</label>
<input type="text" name="author" id="author" class="form-control">
</div>
<div class="form-group">
<label for="book-id">Reference Id</label>
<input type="text" name="title" id="bookId" class="form-control">
</div>
<input type="submit" value="Add Book" class="btn btn-primary w-100 mt-3" id="submit">
</form>
<table class="table table-striped mt-4">
<thead>
<tr>
<th>Book Title</th>
<th>Author Name</th>
<th>Reference Id</th>
<th class="text-warning"><small>Delete book</small></th>
</tr>
</thead>
<tbody id="book-list"></tbody>
</table>
</div>
<script src="app.js" type="text/javascript"></script>
</body>
</html>
Upvotes: 1