Templating engines met Handlebars

Templating engines met Handlebars

INTRODUCTIE

Handlebars is a simple templating language based on {{mustache}}. It uses a template and an input object to generate HTML or other text formats. Handlebars templates look like regular text with embedded Handlebars expressions.

Installatie

Installeer handlebars

npm install handlebars

Installeer express-handlebars

npm install express-handlebars

Voorbereiding

  • Maak volgende folders en bestanden aan in je views folder (opmerking: home.hbs zit in de folder views
  • Typ in home.hbs de tekst Welcome to handlebars!

Voorbereiding

  • Maak een HTML-startstructuur in main.hbs,
    let op de {{{body}}}-syntax!
<!DOCTYPE html>
<html lang="nl">
<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>Mijn eerste handlebars applicatie</title>
</head>
<body>
  {{{body}}}
</body>
</html>

Handlebars - Express

  • Importeer de create functie in je app.js uit de "express-handlebars" package.
import { create } from "express-handlebars";
  • Maak een nieuwe Handlebars instantie, zorg dat express werkt met .hbs bestanden.
const hbs = create({
  extname: "hbs",
});

Handlebars - Express

  • Koppel nu Handlebars aan je gemaakte Express applicatie.
app.engine("hbs", hbs.engine);
app.set("view engine", "hbs");
app.set("views", path.join(SOURCE_PATH, "views"));
  • De variabelen SOURCE_PATH halen we uit een consts.js module.
import * as path from "path";
export const SOURCE_PATH = path.resolve("src");

Handlebars - Express

  • Maak een nieuwe controller:
    PageController.js in de controllers folder
  • Plaats hier een heel eenvoudige functie die een antwoord stuurt naar de client met de render() functie.
export const home = (req, res) => {
  res.render('home');
}

Handlebars - Express

export const home = (req, res) => {
  res.render('home');
}
  • Importeer de home functie en registreer de controller in Express op de GET - "/" route 
  • Plaats hier een heel eenvoudige functie die een antwoord stuurt naar de client met de render() functie.
export const home = (req, res) => {
  res.render('home');
}
app.get('/', home);

Handlebars - Express

  • Wanneer je de server herstart en via je browser de root-route opvraagt, krijg je je tekst te zien. Handlebars is up & running!

Templating engines met Handlebars

EXPRESSIONS

Handlebars expressions are the basic unit of a Handlebars template. You can use them alone in a {{mustache}}, pass them to a Handlebars helper, or use them as values in hash arguments.

Basis Expressions

  • Handlebars expressions zijn elementen die je ziet staan met {{ }}. In onderstaande template is de variabele firstname, omgeven met {{ }}, en dus een expression.
<p>{{firstname}}</p>
  • Geven we een object mee met een property firstname in de render response, dan zal deze ingevuld worden.

Geneste Objecten

{
  person: {
    firstname: "Yehuda",
    lastname: "Katz",
  },
}
{{person.firstname}} {{person.lastname}}

Context

  • Met de {{#with}}-block helper bepalen we de context van de expression.
{{#with person}}
  {{firstname}} {{lastname}}
{{/with}}
  • Met de {{#each}}-block kunnen we itereren over een lijst.
<ul class="people_list">
  {{#each people}}
    <li>{{this}}</li>
  {{/each}}
</ul>

HTML-escaping

  • Wanneer we elementen rond drie curly braces plaatsen: {{{ }}} dan zal de tekst niet omgevormd worden door tekens die "escaped" zijn. 
  • Je kan in deze tool eens bekijken hoe je speciale tekens "escaped" worden.

Templating engines met Handlebars

PARTIALS

Handlebars allows for template reuse through partials. Partials are normal Handlebars templates that may be called directly by other templates.

Basis Partials

  • Je kan een partial registreren door een nieuw .hbs bestand aan te maken in je partials folder.
  • Vervolgens kan je je partial in je template gebruiken met:
{{> myPartial }}

Partial Context

  • Net zoals bij een expression kan je ook aan een partial een context meegeven.
{{> myPartial myOtherContext }}

Templating engines met Handlebars

App structuur optimaliseren

In onze app vind je afbeeldingen van dinosaurussen. Je kunt routes gebruiken om naar een overzicht van dinosaurussen te gaan en vervolgens door te klikken naar een detailpagina.

Een scenario, waar een aparte DinoController aangewezen is.

We hebben ook gedeelde data die in beide pagina's nodig is.

Data afzonderen -> data.js

  • Creëer een js-bestand waar je json-data kan exporteren: src/data/data.js
  • Maak daar een array aan met informatie over 8 dino's, met minstens volgende properties
    • id
    • title
    • slug
    • image ( vb /assets/images/dino_03.png)
    • description
  • ​Exporteer de data via export const dinos ={...}

DinoController maken

  • Maak een DinoController aan, met daarin twee actions = methodes die instaan voor afhandeling van express requests)


     
  • De index-methode zal instaan voor het overzicht van alle dino's, de detail voor de details van elke dino
export const index = (req, res) => {};
export const detail = (req, res) => {};
  • Importeer de dino-data in die controller

DinoController maken

  • Importeer de dino-data in die controller
  • Render voor beide actions (index en detail) andere views uit
  • Volgende stap: routes opvangen via app.js
    (volgende slide)

Routes registreren

  • In app.js maak je twee routes aan,
    die volgende url's opvangen
    • /dinosaurs
      --> naar index-methode uit DinoController
    • /dinosaurs/:slug
      --> naar detail-methode uit DinoController
       
  • ":slug" is een route parameter (link)
  • ​Test uit door naar de twee routes te gaan

Oefening

  • Maak een navigatiebalk aan, die het mogelijk stelt om naar de pagina's te navigeren
    • home
    • contact
    • dinosaurs
  • Vermijd redundante code, dus zorg voor een goede data-structuur, partials, ...

Templating engines met Handlebars

BUILT-IN HELPERS

#if

<div class="entry">
{{#if author}}
  <h1>{{firstName}} {{lastName}}</h1>
{{/if}}
</div>
{
  author: true,
  firstName: "Yehuda",
  lastName: "Katz",
}
<div class="entry">
  <h1>Yehuda Katz</h1>
</div>

#unless

  • Je kan {{#unless}} gebruiken als omgekeerde variant van {{#if}}. Het block zal gerenderd worden wanneer de expression false is.
<div class="entry">
  {{#unless license}}
    <h3 class="warning">
      WARNING: This entry does not have a license!
    </h3>
  {{/unless}}
</div>

#each

Itereren over een lijst doe je met {{#each}}

<ul class="people_list">
  {{#each people}}
    <li>{{this}}</li>
  {{/each}}
</ul>
{
  people: [
    "Yehuda Katz",
    "Alan Johnson",
    "Charles Jolley",
  ],
}
<ul class="people_list">
    <li>Yehuda Katz</li>
    <li>Alan Johnson</li>
    <li>Charles Jolley</li>
</ul>

#with

  • Met deze helper kan je de context wijzigen.
{{#with person}}
  {{firstname}} {{lastname}} 
{{/with}}
{
  person: {
    firstname: "Yehuda",
    lastname: "Katz",
  },
}

Output: Yehunda Katz

Templating engines met Handlebars

CUSTOM BLOCK HELPERS

Block helpers make it possible to define custom iterators and other functionality that can invoke the passed block with a new context.

Custom Block Helpers

  • We definiëren naast de bestaande block helpers ({{#each}}, {{#with}}, {{#if}} en {{#unless}}) op maat gemaakte block helpers.
const hbs = create({
  helpers: HandlebarsHelpers,
  extname: "hbs",
});

Voorbeeld

<h1>
  Welcome to handlebars, {{#bold}}{{firstname}}!{{/bold}}
</h1>
import handlebars from "handlebars";
const { SafeString } = handlebars;

export default {
  bold: function(options) {
    return new SafeString(`<strong>${options.fn()}</strong>`);
  }
};

HandleBarsHelpers.js

Voorbeeld

De options.fn() functie gedraagt zich als een gecompileerde versie van de Handlebars template. De functie neemt als parameter een context mee.

Voorbeeld

import handlebars from "handlebars";
const { SafeString } = handlebars;

export default {
  bold: function(options) {
    return new SafeString(
    	`<strong>${options.fn({ firstname: "Eveline"})}</strong>`
    );
  }
};

HandleBarsHelpers.js

Voorbeeld

Resulteert in

Voorbeeld

import handlebars from "handlebars";
const { SafeString } = handlebars;

export default {
  bold: function(context, options) {
    return new SafeString(
      `<strong>${context} ${options.fn({ firstname: "Eveline"})}</strong>`
    );
  }
};

HandleBarsHelpers.js

Text

<h1>
  Welcome to handlebars, {{#bold "Mrs."}}{{firstname}}!{{/bold}}
</h1>

Voorbeeld

Resulteert in

Custom Helpers

loud: (text) => text.toUpperCase()
<h1>
  Welcome to {{loud "handlebars"}}, 
  {{#bold "Mrs."}}{{firstname}}!{{/bold}}
</h1>

Oefening

  • Maak een block helper die een tekst cursief zet
  • Maak een block helper voor een button
  • Maak een block helper voor een link
  • Maak een block helper die gelijk staat aan de {{#if}} helper
  • Maak een block helper die gelijk staat een een {{#each}}

Oefening

  • Maak een block helper die als context een lijst van comments meekrijgt en de uit te renderen template zal voeden. 
{{#commentList comments}}
  <li>{{title}}</li>
{{/commentList}}

PGM3/3 - Templating Engines met Handlebars

By Frederick Roegiers

PGM3/3 - Templating Engines met Handlebars

  • 52