Aplikacja ma funkcjonalność bez zmian (patrz Rozdział 6).
Aplikacja została zaimplementowana w oparciu o technologię AJAX z wykorzystaniem funkcji fetch()
Struktura plików aplikacji podana jest na poniższym obrazku.
. ├── db │ └── books.js ├── Gruntfile.js ├── package.json ├── public │ ├── css │ │ └── styl.css │ ├── favicon.ico │ ├── index.html │ └── js │ └── script.js └── serwer.js
Główne pliki w tej implementacji — to: index.html,script.js, serwer.js.
index.htmlTen plik jest wysyłany do klienta bez zmian. Zawiera on podstawowe drzewo DOM strony. Drzewo to będzie uzupełniane i modyfikowane dynamiczne po stronie klienta.
script.jsTen plik zawiera aplikację kliencką.
serwer.jsW tym pliku jest aplikacja serwerowa.
Aplikacja serwerowa jest podobna do poprzedniej. Ponieważ całe drzewo DOM buduje się po stronie klienta, serwerze wysyła tylko dane: listę gatunków:
app.get('/genres', function (req, res) {
let genres = baza().distinct("genre");
res.json(genres);
});
Oraz listę książek określonego gatunku:
app.get('/genre/:gen', function (req, res) {
let books = baza({genre: req.params.gen}).select("title", "author");
res.json(books);
});
Dodatkowo aplikacja wykorzystuje moduł express-session:
let session = require('express-session');
app.use(session({
secret: 'xxxyyyzzz',
resave: false,
saveUninitialized: true
}));
Ten moduł odpowiada za identyfikację klientów. W każdym zapytaniu teraz jest dostępna właściwość session, do której można dopisywać/odczytywać dane, na przykład,
app.post('/login', function (req, res) {
req.session = req.body.login;
…
Albo:
app.post('genre/:gen', function (req, res) {
if( req.session == 'admin'){
…
W momencie, kiedy drzewo DOM już jest zbudowano, wywołana jest funkcja setup:
document.addEventListener('DOMContentLoaded', setup);
Zadaniem tej funkcji jest uzupełnić elementy html,
function setup() {
let form = document.querySelector('form');
form.onsubmit = postBook;
fetch( 'genres')
.then(
response => {
if(response.ok){
return response.json();
}
else{
throw new Error("Błąd fetch genres: " + response.status);
}
}
)
.then(
genres => {
let ul = document.querySelector('nav ul');
for (let g of genres){
let li = document.createElement('li');
li.textContent = g;
li.className = 'clickable';
li.onclick = getShowGenre(g);
ul.append(li);
}
}
)
.catch( error => alert(error) );
}
Mianowicie:
submit formularza funkcję postBook
li, zarejestrować na zdarzeniu click tego elementu funkcję oraz dodać ten element do listy ul.
function(){
fetch('genre/'+ genre)
.then(
response => {
if(response.ok){
return response.json();
}
else{
throw new Error("Błąd fetch one genre: " + response.status);
}
}
)
.then(
books => {
let h1= document.querySelector('h1');
h1.className = 'clickable';
h1.onclick = goRoot;
let nav = document.querySelector('nav');
nav.className='small';
let section = nav.nextElementSibling;
section.style.display = 'block';
let h2 = section.firstElementChild;
h2.textContent = genre;
let ul = h2.nextElementSibling;
while (ul.lastChild) ul.lastChild.remove();
for (let b of books){
let author = document.createElement('span');
author.className = 'author';
author.textContent = b[1];
let title = document.createElement('span');
title.className = 'title';
title.textContent = b[0];
let li = document.createElement('li');
li.append(author);
li.append(document.createTextNode(': '));
li.append(title);
ul.append(li);
}
}
)
.catch( error => alert(error) );
}clickable i zarejestrować na kliknięcie funkcję goRoot, która przełączy aplikację w tryb strony głównej
small
section
h2
ul i wypełnić go listą książek
function goRoot(){
let h1= document.querySelector('h1');
h1.className = '';
h1.onclick = null;
let nav = document.querySelector('nav');
nav.className='';
let section = nav.nextElementSibling;
section.style.display = 'none';
}small
section