LEKCJA 05 - EXPRESS (p) - szablony Handlebars /updated 3.12/

LEKCJA 05 - EXPRESS (p) - szablony Handlebars /updated 3.12/
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

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

6. ćwiczenia

ć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)
- 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