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.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ę 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