ukryj menu
SPEC
aktualizacja: 2021-12-03 11:59:35
1. server templating (szablony po stronie serwera)
Szablony po stronie serwera:
- umożliwiają renderowanie kodu HTML PRZED wysłaniem go do klienta
- umożliwiają dołączenie danych js z serwera, do struktury strony HTML
- klient/użytkownik otrzymuje wynikowy kod HTML bez możliwości dokonywania w nim zmian (bezpieczeństwo)
- klient/użytkownik nie zobaczy szablonu po stronie serwera, ani danych użytych do wygenerowania ostatecznego kodu HTML (bezpieczeństwo)
- HTML jest renderowany na podstawie danych (obiektów) przetwarzanych na serwerze
popularne silniki szablonów:
jade, ejs, handlebars, pug
2. bazowa struktura aplikacji używającej szablonów Handlebars
{appDir}
|
|__ static
| |__ css
| |
| |__ gfx
| |
| |__ js
|
|__ views
| |__ layouts
| | |__ main.hbs
| |
| |__ view1.hbs // czyli podstrona html z rozszerzeniem .hbs
| |__ view2.hbs
| |__ view3.hbs
|
|__ server.jskopiuj
3. składowe szablonów handlebars - context, layouts, views
a) context - dane javascript, obiekt lub tablica obiektów, wszystko zależy od danych które mamy do dyspozycji - najczęściej o strukturze:
const context = {
t:[
{a:1, b:1},
{a:2, b:2}
]
}kopiuj
b) views - widoki
- widok zwykle reprezentujący pojedynczą stronę
- domyślnie Handlebars szuka widoków w podkatalogu /views
c) layout
- to szablon dla widoków
- jest niezbędny, ponieważ większość stron w witrynie ma niemal identyczny układ, np <html> i element <title>, zwykle ładują te same pliki CSS, etc
4. pliki bazowej aplikacji
plik layout / main.hbs
- szablon powtarzający się na każdej stronie
- element {{{body}}} oznacza, że w to właśnie miejsce włączone zostaną widoki z katalogu /views
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
{{{body}}}
</body>
</html>kopiuj
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
{{{body}}}
</body>
</html>kopiuj
plik views / index.hbs
- dowolna zawartość html renderowana do {{{body}}} w layouts / main.hbs
- w przypadku tworzenia widoków opartych o dane renderowane na podstawie js, w widokach pobieramy je za pomocą podwójnych nawiasów, np: {{title}} o czym w dalszej części
5. szablony handlebars - instalacja na serwerze nodejs
npm install express-handlebars@5.3.5 kopiuj
użycie z expressem
var hbs = require('express-handlebars');kopiuj
przed app.listen(...)
app.set('views', path.join(__dirname, 'views')); // ustalamy katalog views
app.engine('hbs', hbs({ defaultLayout: 'main.hbs' })); // domyślny layout, potem można go zmienić
app.set('view engine', 'hbs'); kopiuj // określenie nazwy silnika szablonów
get
app.get("/", function (req, res) {
res.render('index.hbs'); // nie podajemy ścieżki tylko nazwę pliku
// res.render('index.hbs', { layout: "main.hbs" }); // opcjonalnie podajemy konkretny layout dla tego widoku
})kopiuj
jeśli nie chcemy żadnego layoutu dla danego widoku
app.get("/", function(req, res){
res.render('index.hbs', { layout: null });
});kopiuj
UWAGA: najnowsza wersja Handlebars ma zmienione api
https://github.com/ericf/express-handlebars
ćwiczenie 1 - dwie strony .hbs
utwórz plik server01.js
- w zależności od adresów /index lub /login wyświetlaj różne strony : index.hbs lub login.hbs
- struktura katalogów jak w punkcie 2
ćwiczenie 2 - strona z podstawowym kontextem js w pliku serwera
umieść w pliku server02.js taki obiekt:
const context = {
subject: "ćwiczenie 2 - podstawowy context",
content: "to jest TREŚĆ mojej strony",
footer: "to jest stopka na mojej stronie"
}kopiuj
dodanie contextu do wysyłanego pliku hbs
res.render('index.hbs', context);kopiuj
utwórz odpowiednią strukturę plików / katalogów (patrz pkt 2)
- wyświetl stronę index02.hbs z odpowiednimi danymi pobranymi z kontextu, jak poniżej:
{{subject}}kopiuj
- style w pliku static/css/style.css
ćwiczenie 3 - strona z danymi z tablicy obiektów
umieść w pliku server03.js obiekt:
const context = {
subject: "ćwiczenie 3 - dane z tablicy obiektów",
books: [
{ title: "Lalka", author: "B Prus", lang: "PL" },
{ title: "Hamlet", author: "W Szekspir", lang: "ENG" },
{ title: "Pan Wołodyjowski", author: "H Sienkiewicz", lang: "PL" },
{ title: "Dwór mgieł i furii", author: "S.J. Maas", lang: "CZ" }
]
}kopiuj
- utwórz odpowiednią strukturę katalogów (patrz pkt 2)
- wyświetl stronę index03.hbs z danymi pobranymi z kontextu:
{{#each books}}
{{title}}
{{/each}}kopiuj
- subject to znacznik h1
- każdy title jest znacznikiem h3
- style w pliku static/css/style.css
ćwiczenie 4 - formularz z selectem
utwórz plik server04.js
dodaj taki obiekt:
const context = {
subject: "ćwiczenie 4 - dane z tablicy, select",
fields:[
{name:"title"},
{name:"author"},
{name:"lang"}
],
books: [
{ title: "Lalka", author: "B Prus", lang: "PL" },
{ title: "Hamlet", author: "W Szekspir", lang: "ENG" },
{ title: "Pan Wołodyjowski", author: "H Sienkiewicz", lang: "PL" },
{ title: "Zamek", author: "F Kafka", lang: "CZ" }
]
}kopiuj
teraz wyświetl dla testów i zrozumienia jak to działa, powyższe dane na serwerze:
console.log("--------------- czytam dane z kontextu")
console.log("--- cały obiekt context")
console.log(context)
console.log("--- tablica fields z obiektu context")
console.log(context.fields)
console.log("--- tablica books z obiektu context")
console.log(context.books)
console.log("--- zerowy index z tablicy books")
console.log(context.books[0])
console.log("--- pole title z zerowego indeksu z tablicy books")
console.log(context.books[0].title)
console.log("--------------- koniec czytania kontextu") kopiuj
utwórz stronę index04.hbs
- dodaj na stronie formularz wysyłany get-em, selecta i submita
- opcje selecta utwórz z danymi pobranymi z kontextu
- po wysłaniu formularza, wraca z serwera JEDNA strona index041.hbs z danymi, które wybierzemy w select: title lub author lub lang
- pomoc: użyj switch-a zamiast if-a, zwracaj inny context w każdym case
ćwiczenie 5 - formularz z radios
- zamiast selecta -> generuje się lista radios
- gdy nie wybrano radio - odpowiedni komunikat
ćwiczenie 6 - formularz z checkboxami
- zamiast selecta -> generuje się lista checkboxów
- po zaznaczeniu kilku checkboxów otrzymujemy kilka informacji (np author i title, author i lang)
ćwiczenie 6 - formularz z checkboxami
- zamiast selecta -> generuje się lista checkboxów
- po zaznaczeniu kilku checkboxów otrzymujemy kilka informacji (np author i title, author i lang)
- gdy nie wybrano checkboxa - odpowiedni komunikat
7. dane w osobnym pliku - require, JSON
utwórz plik server07.js
kontext warto trzymać w osobnym pliku, a nie w samym pliku serwera
{appDir}
|
|__ data
| |__ data.json
|
|__ static
|
|__ views
|
|__ server.jskopiuj
w takim przypadku dane w pliku data.json będą w formacie JSON
o formacie JSON
http://www.json.org/
parser sprawdzający czy dane są w postaci JSON
https://jsonlint.com/
wstaw do pliku data/data07.json poniższe dane
{
"subject": "ćwiczenie 7 - render tablic, obiektów, tablic obiektów",
"countries": [ "Russia", "India", "USA" ],
"langs": [ "polski", "angielski", "japoński" ],
"digits": [[1,2],[3,4],[5,6]],
"user": {
"login": "aaa",
"password": "bbb",
"age": 16
},
"books": [
{ "title": "Lalka" },
{ "title": "Proces" },
{ "title": "Pan Wołodyjowski" }
]
}kopiuj
pobierz do aplikacji obiekt z pliku:
const context = require("./data/data07.json")
console.log(context)kopiuj
- utwórz stronę index07.hbs wyświetlającą dane jak na obrazku na spec-u
- style w pliku static/css/style.css
do przetwarzania kontextu będą potrzebne poniższe instrukcje handlebars:
wyświetlenie całej tablicy 1 lub 2-wymiarowej
{{langs}}
{{digits}}kopiuj
pobranie elementu z tablicy 1 lub 2-wymiarowej
{{countries.[0]}}
{{digits.[0].[1]}}kopiuj
iteracja przez tablicę
{{#each countries}}
{{@index}} {{this}}
{{/each}}kopiuj
iteracja przez obiekt
{{#each user}}
{{@index}} {{@key}} {{this}}
{{/each}}kopiuj
iteracja przez tablicę obiektów
{{#each books}}
{{@index}} {{title}}
{{/each}}kopiuj
8. if...else /if
utwórz plik server08.js
wstaw do pliku data/data08.json poniższe dane
{
"subject": "ćwiczenie 8 - if...else",
"books": [
{ "title": "Lalka","lang": "PL", "author": "B Prus" },
{ "title": "Hamlet", "lang": "ENG", "author": "W Szekspir" },
{ "title": "Pan Wołodyjowski", "lang": "PL" }
]
}kopiuj
pobierz do aplikacji obiekt z pliku:
const context = require("./data/data08.json")
console.log(context)kopiuj
- utwórz stronę index08.hbs wyświetlającą dane, jak na obrazku na spec-u
- style w pliku static/css/style.csskopiuj
do przetwarzania kontextu będzie potrzebna instrukcja handlebars if...else
{{#if author }}
{{author}} {{title}}
{{else}}
brak autora
{{/if}}kopiuj
9. helpers - przetwarzanie kontextu po stronie serwera
utwórz plik server09.js
wstaw do pliku data/data09.json poniższe dane
{
"subject": "T: ćwiczenie 9 - użycie helperów",
"title": "Lorem ipsum dolor sit amet consectetur adipiscing elit"
}kopiuj
tworzenie helpera na serwerze (uwaga - podmieniamy app.engine )
app.engine('hbs', hbs({
defaultLayout: 'main.hbs' ,
helpers: {
shortTitle: function (title) {
return title.substring(0,10) +"...";
},
innyHelper: function (title) {
//...
},
}
}));kopiuj
użycie helpera w pliku .hbs
{{shortTitle title}}kopiuj
dodaj dwa kolejne helpery, przetwarzające kontext - wg rysunku
10. helpers - przetwarzanie kontextu po stronie serwera - formularz
utwórz plik server10.js
wstaw do pliku data/data10.json poniższe dane
{
"subject": "T: ćwiczenie 10 - użycie helperów cz2",
"title": "Lorem ipsum dolor sit amet consectetur adipiscing elit"
}kopiuj
wykonaj formularz odsyłający do tej samej strony,
input pobiera dane do przetwarzania przez helper,
taki sam jak w poprzednim ćwiczeniu
11. partials - oddzielny plik .hbs z powtarzalnym fragmentem kodu html
nadaje się dobrze do renderowania powtarzalnych elementów aplikacji (np wiersza w liście)
{appDir}
|
|__ views
| |__ partials
|
|__ server.js
kopiuj
utwórz plik server11.js
wstaw do pliku data/data11.json poniższe dane
{
"subject": "T: ćwiczenie 11 - partial w osobnym pliku hbs",
"books": [
{ "title": "Lalka Prusa", "lang": "PL", "image": "lalka" },
{ "title": "Hamlet Szekspira", "lang": "ENG", "image": "hamlet" },
{ "title": "Krzyżacy Sienkiewicza", "lang": "PL", "image": "krzyzacy" }
]
}kopiuj
ustalenie lokalizacji plików partial w katalogu views/partials
app.engine('hbs', hbs({
extname: '.hbs',
partialsDir: "views/partials",
}));kopiuj
plik partial views/partials/jedenbook.hbs
tytuł: {{title}} - {{lang}} - {{image}}kopiuj
użycie partiala w pliku strony index11.hbs
{{#each books}}
{{> jedenbook}}
{{/each}}kopiuj
- dodaj pliki jpg do katalogu /static/gfx
- style w pliku static/css/style.css
12. dokumentacja handlebars
http://handlebarsjs.com/kopiuj
7. dane w osobnym pliku - require, JSON
utwórz plik server07.js
kontext warto trzymać w osobnym pliku, a nie w samym pliku serwera
{appDir}
|
|__ data
| |__ data.json
|
|__ static
|
|__ views
|
|__ server.jskopiuj
w takim przypadku dane w pliku data.json będą w formacie JSON
o formacie JSON
http://www.json.org/
parser sprawdzający czy dane są w postaci JSON
https://jsonlint.com/
wstaw do pliku data/data07.json poniższe dane
{
"subject": "ćwiczenie 7 - render tablic, obiektów, tablic obiektów",
"countries": [ "Russia", "India", "USA" ],
"langs": [ "polski", "angielski", "japoński" ],
"digits": [[1,2],[3,4],[5,6]],
"user": {
"login": "aaa",
"password": "bbb",
"age": 16
},
"books": [
{ "title": "Lalka" },
{ "title": "Proces" },
{ "title": "Pan Wołodyjowski" }
]
}kopiuj
pobierz do aplikacji obiekt z pliku:
const context = require("./data/data07.json")
console.log(context)kopiuj
- utwórz stronę index07.hbs wyświetlającą dane jak na obrazku na spec-u
- style w pliku static/css/style.css
do przetwarzania kontextu będą potrzebne poniższe instrukcje handlebars:
wyświetlenie całej tablicy 1 lub 2-wymiarowej
{{langs}}
{{digits}}kopiuj
pobranie elementu z tablicy 1 lub 2-wymiarowej
{{countries.[0]}}
{{digits.[0].[1]}}kopiuj
iteracja przez tablicę
{{#each countries}}
{{@index}} {{this}}
{{/each}}kopiuj
iteracja przez obiekt
{{#each user}}
{{@index}} {{@key}} {{this}}
{{/each}}kopiuj
iteracja przez tablicę obiektów
{{#each books}}
{{@index}} {{title}}
{{/each}}kopiuj
8. if...else /if
utwórz plik server08.js
wstaw do pliku data/data08.json poniższe dane
{
"subject": "ćwiczenie 8 - if...else",
"books": [
{ "title": "Lalka","lang": "PL", "author": "B Prus" },
{ "title": "Hamlet", "lang": "ENG", "author": "W Szekspir" },
{ "title": "Pan Wołodyjowski", "lang": "PL" }
]
}kopiuj
pobierz do aplikacji obiekt z pliku:
const context = require("./data/data08.json")
console.log(context)kopiuj
- utwórz stronę index08.hbs wyświetlającą dane, jak na obrazku na spec-u
- style w pliku static/css/style.csskopiuj
do przetwarzania kontextu będzie potrzebna instrukcja handlebars if...else
{{#if author }}
{{author}} {{title}}
{{else}}
brak autora
{{/if}}kopiuj
9. helpers - przetwarzanie kontextu po stronie serwera
utwórz plik server09.js
wstaw do pliku data/data09.json poniższe dane
{
"subject": "T: ćwiczenie 9 - użycie helperów",
"title": "Lorem ipsum dolor sit amet consectetur adipiscing elit"
}kopiuj
tworzenie helpera na serwerze (uwaga - podmieniamy app.engine )
app.engine('hbs', hbs({
defaultLayout: 'main.hbs' ,
helpers: {
shortTitle: function (title) {
return title.substring(0,10) +"...";
},
innyHelper: function (title) {
//...
},
}
}));kopiuj
użycie helpera w pliku .hbs
{{shortTitle title}}kopiuj
dodaj dwa kolejne helpery, przetwarzające kontext - wg rysunku
10. helpers - przetwarzanie kontextu po stronie serwera - formularz
utwórz plik server10.js
wstaw do pliku data/data10.json poniższe dane
{
"subject": "T: ćwiczenie 10 - użycie helperów cz2",
"title": "Lorem ipsum dolor sit amet consectetur adipiscing elit"
}kopiuj
wykonaj formularz odsyłający do tej samej strony,
input pobiera dane do przetwarzania przez helper,
taki sam jak w poprzednim ćwiczeniu
11. partials - oddzielny plik .hbs z powtarzalnym fragmentem kodu html
nadaje się dobrze do renderowania powtarzalnych elementów aplikacji (np wiersza w liście)
{appDir}
|
|__ views
| |__ partials
|
|__ server.js
kopiuj
utwórz plik server11.js
wstaw do pliku data/data11.json poniższe dane
{
"subject": "T: ćwiczenie 11 - partial w osobnym pliku hbs",
"books": [
{ "title": "Lalka Prusa", "lang": "PL", "image": "lalka" },
{ "title": "Hamlet Szekspira", "lang": "ENG", "image": "hamlet" },
{ "title": "Krzyżacy Sienkiewicza", "lang": "PL", "image": "krzyzacy" }
]
}kopiuj
ustalenie lokalizacji plików partial w katalogu views/partials
app.engine('hbs', hbs({
extname: '.hbs',
partialsDir: "views/partials",
}));kopiuj
plik partial views/partials/jedenbook.hbs
tytuł: {{title}} - {{lang}} - {{image}}kopiuj
użycie partiala w pliku strony index11.hbs
{{#each books}}
{{> jedenbook}}
{{/each}}kopiuj
- dodaj pliki jpg do katalogu /static/gfx
- style w pliku static/css/style.css
12. dokumentacja handlebars
http://handlebarsjs.com/kopiuj