-
Notifications
You must be signed in to change notification settings - Fork 7
The static website with vanilla javascript
In this example I will show how to build something very simple. A table with books rendered with vanilla javascript. More dynamic functionality will be implemented in example3
First we start with a model for authors:
class AuthorModel{
/**
* @param {id: Number, name: String}
* @return AuthorModel
*/
constructor(obj){
this.updateProperties(obj);
}
/**
* Map properties to this instance
*
* @param {id: Number, name: String}
* @return void
*/
updateProperties(obj){
this.id = obj.id;
if(obj.name) this.name = obj.name;
}
}
We also need a model for books:
class BookModel{
/**
* @param {id: Number, author: String, title: String, isbn: String}
* @return AuthorModel
*/
constructor(obj) {
this.updateProperties(obj);
}
/**
* Map properties to this instance
*
* @param {id: Number, author: String, title: String, isbn: String}
* @return void
*/
updateProperties(obj) {
this.id = obj.id;
if (obj.author) this.author = obj.author;
if (obj.title) this.title = obj.title;
if (obj.isbn) this.isbn = obj.isbn;
}
/**
* Get a list of properties for this class
*
* @returns {string[]}
*/
static getFields() {
return ['id', 'title', 'isbn', 'author'];
}
}
Notice the updateProperties method. The idea behind this is that when you need to update an instance of this class you run this method. I will explain more later why I've done this. But for now don't worry too much.
Another thing I would like to point out. When I update an instance I want to update all fields. The reason for this is simplicity. There is no reason to over-engineer with setters and getters. It will also make more sense later.
Finally we need to implement the table component
/**
* Book table component. We call this a component as its behaviour is a
* reusable component for web composition.
*
* With this design it is also easier to map it over to a true web-component,
* which will hopefully soon become a standard in all the major browsers.
*/
class BookTableComponent{
constructor(obj){
this.containerElement = obj.containerElement;
this.fields = BookModel.getFields();
this.updateProperties(obj);
this.buildDOMElements();
this.render();
}
updateProperties(obj) {
this.books = obj.books;
}
buildDOMElements() {
this.tableElement = document.createElement('TABLE');
this.tableHeaderElement = this.tableElement.createTHead();
// There is no createTBody function
this.tableBodyElement = document.createElement('TBODY');
this.tableElement.appendChild(this.tableBodyElement);
}
renderHead(){
// map() will loop the fields property and create the <th> elements
this.tableHeaderElement.innerHTML = `
<tr>
${this.fields.map(item => `<th>${item}</th>`).join('')}
</tr>
`;
}
renderBody(){
this.tableBodyElement.innerHTML = `
${this.books.map(book => `
<tr>
<td>${book.id}</td>
<td>${book.title}</td>
<td>${book.isbn}</td>
<td>${book.author.name}</td>
</tr>
`).join('')}
`;
}
render(){
this.renderHead();
this.renderBody();
this.containerElement.innerHTML = "";
this.containerElement.appendChild(this.tableElement);
}
}
This is all the code you need to render books into a table. We build the DOM elements, and render the table header and table body. The most interesting part here is the template literals with a loop using map().
All you need now is a html page to show the results. Lets create books.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>List of books</title>
<link type="text/css" rel="stylesheet" href="css/style.css"/>
</head>
<body>
<section>
<h1>Book Administration with Vanilla JS</h1>
<div data-container="books-table"></div>
</section>
<script type="application/javascript" src="js/application.js"></script>
<script type="application/javascript">
// Create book objects
let books = [
new BookModel({
id: 1,
title: 'Horror stories with React',
author: new AuthorModel({name: 'Alfred Angular'}),
isbn: "1111-222222-6666"
}),
new BookModel({
id: 2,
title: 'The Angular Fiasco',
author: new AuthorModel({name: 'Robert React'}),
isbn: "1331-123456-7777"
}),
new BookModel({
id: 3,
title: 'The Vue Skyfall',
author: new AuthorModel({name: 'Vanessa Vanilla'}),
isbn: "1411-987654-6666"
}),
new BookModel({
id: 4,
title: 'jQuery the forgotten gem',
author: new AuthorModel({name: 'Ivar Explorer'}),
isbn: "2111-123789-5678"
}),
new BookModel({
id: 5,
title: 'Angular Reboot 3',
author: new AuthorModel({name: 'Steven Syntax'}),
isbn: "3311-000112-4545"
})
];
// Find all DOM elements which we want to render to
const bookTables =
document.querySelectorAll('[data-container="books-table"]');
for(let i = 0; i < bookTables.length; i++){
// Initialize the table component
new BookTableComponent({
books: books,
containerElement: bookTables[i]
});
}
</script>
</body>
</html>
Check the example1 folder for the complete working source code. When all this is implemented you will have the following table:
Next step is to implement state management. We need to make it possible to add, edit and delete books and reflect changes in the DOM. Head over to example2.