Aplikacja ma funkcjonalność bez zmian (patrz Rozdział 9).
Aplikacja została zaimplementowana w oparciu o technologię AJAX z wykorzystaniem funkcji fetch()
Struktura plików aplikacji podana jest na poniższym obrazku.
. ├── db │ └── products.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ę kategorii:
app.get('/categories', function (req, res) { let cats = baza().distinct("category"); res.json(cats); });
Oraz listę produktów określonej kategorii:
app.get('/category/:cat', function (req, res) { let products = baza({category: req.params.cat}).select("product", "price"); res.json(products); });
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.login = req.body.login; …
Albo:
app.post('category/:cat', function (req, res) { if( req.session.login == '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 = postProduct; fetch( 'categories') .then( response => { if(response.ok){ return response.json(); } else{ throw new Error("Błąd fetch categories: " + response.status); } } ) .then( categories => { let ul = document.querySelector('nav ul'); for (let c of categories){ let li = document.createElement('li'); li.textContent = c; li.className = 'clickable'; li.onclick = getShowCategory(c); ul.append(li); } } ) .catch( error => alert(error) ); }
Mianowicie:
submit
formularza funkcję postProduct
li
, zarejestrować na zdarzeniu click
tego elementu funkcję oraz dodać ten element do listy ul
.
function(){ fetch('category/'+ cat) .then( response => { if(response.ok){ return response.json(); } else{ throw new Error("Błąd fetch one category: " + response.status); } } ) .then( products => { 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 = cat; let ul = h2.nextElementSibling; while (ul.lastChild) ul.lastChild.remove(); for (let pr of products){ let product = document.createElement('span'); product.className = 'product'; product.textContent = pr[1]; let price = document.createElement('span'); price.className = 'price'; price.textContent = pr[0]; let li = document.createElement('li'); li.append(product); li.append(document.createTextNode(': ')); li.append(price); 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ą produktów
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