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.html
Ten 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.js
Ten plik zawiera aplikację kliencką.
serwer.js
W 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