Bisher: Werte in die Seite einsetzen (Interpolation)

Zur Erinnerung: {{page.vorname}} setzt den Wert vorname aus dem Frontmatter einer Seite ein
Funktioniert für Zeichenketten, Zahlen und Warheitswerte
  • framework_projects/vorlesung/pages/interpolation-basic-2-yaml.hbs
    vorname: "Harry"
    nachname: "Potter"
    ---
    <p>
      Der Name ist {{ page.nachname }},
      <em>{{ page.vorname }}</em> {{ page.nachname }}.
    </p>
    
Frage:

Was war nochmal Frontmatter?

Antwort:

Den Daten-Bereich über dem HTML-Code

Neu: Veränderungen an der Struktur des Dokuments

Gewisse Teile der Seite sollen ein- oder ausgeblendet werden
  • Informationen nur für angemeldete Benutzer
  • Rabatt-Aktionen nur für bestimmte Artikel
  • Gefahrhinweise nur für bestimmte Produkte
  • “☠️ Sie sind tot ☠️” nur auf bestimmten Seiten
Strukturell identische Informationen sollen wiederholt werden
  • Liste von Zutaten für ein Rezept (Zutat und Menge)
  • Aufzählung von Volleyballspielern (Name, Rückennummer, Position)

Listen & Schleifen

Daten: Listen ermöglichen die Angaben von vielen Werten
  • Werte werden als eingerückte Liste mit - als Aufzählungszeichen notiert
  • Reihenfolge der Elemente ist relevant
  • Doppelte Elemente sind erlaubt
Template: Schleifen ermöglichen die Ausgaben von vielen Werten
  • #each ermöglicht die Iteration über Listen
  • Inhalt des #each-Blocks wird für jedes Element der Liste einmal ausgeführt
Template: Daten stehen im #each-Bereich ohne Präfix zur Verfügung
  • Keine gesonderte Iterationsvariable
  • Zugriff auf primitive Werte mit this

Beispiel zu Listen & Schleifen

  • framework_projects/vorlesung/pages/loop-basics-yaml.hbs
    dinge:
      - Hase
      - Katze
      - 🐈
      - true
      - Hase
      - 13
      - 💩
    ---
    <h1>Auflistung von Dingen</h1>
    <ol>
      {{#each page.dinge }}
        <li>Ding: {{ this }}</li>
      {{/each}}
    </ol>
Frage:

Warum sind die Dinge in der Ausgabe nummeriert?

Antwort:

<ol> sorgt für Nummerierung.

Objekte in Listen

Statt einfacher Werte: Auch Objekte lassen sich in listen notieren
Müssen “hinter” dem - eingerückt werden
Werte stehen dann dann wie gewohnt mit der .-Notation zur Verfügung
Allerdings muss vor dem . natürlich this stehen
  • framework_projects/vorlesung/pages/loop-basic-objects-yaml.hbs
    personen:
      - name: Bibi Blocksberg
        job: Hexe
      - name: Kartoffelbrei
        job: Besen
    ---
    <ul>
      {{#each page.personen}}
        <li><q>{{ this.name }}</q> ist von Beruf <emph>{{ this.job }}</emph></li>
      {{/each}}
    </ul>
    

Exkurs: JSON-Notation

Bisher gezeigt: YAML-Schreibweise im Frontmatter
Erlaubt Zeilenumbrüche und ist damint insbesondere für SQL sehr viel bequemer
Alternativ möglich: JSON-Schreibweise
Erlaubt kompaktere Notation von Objekten und Listen
Grundsätzlich gleiche Ausdrucksmöglichkeiten, nur andere Notation
Jedes gültige JSON-Objekt ist auch ein gültiges YAML-Objekt
Objekte werden in geschweiften Klammern notiert
  • Die Name-Wert-Paare werden durch Kommata getrennt
  • Zeichenketten müssen zwingend in Anführungszeichen notiert werden
  • { "name": "Bibi Blocksberg", "job": "Hexe", "age": 9 }
Listen werden in eckigen Klammern notiert
  • Wert werden durch Kommata getrennt
  • [1, 2, 3, "ganz viele", ["unter", "liste"], { "ein": "objekt"} ]

Listen mit Objekten mit Listen

Listen dürfen beliebige Werte enthalten, auch Objekte mit weiteren Listen
Konvention: Werte einer Liste sind gleichartig
  • framework_projects/vorlesung/pages/loop-objects.hbs
    {
      "personen": [
        {
          "hobbies": ["lesen", "schwimmen", "reiten"],
          "name": "Thomas"
        },
        {
          "name": "Annika",
          "hobbies": ["Xbox", "PC"]
        }
      ]
    }
    ---
    {{#each page.personen}}
      <p>
        Mein Name ist {{ this.name }} und meine Hobbies sind
        <ul>
          {{#each this.hobbies }}
            <li>{{ this }}</li>
          {{/each}}
        </ul>
      </p>
    {{/each}}

Listen mit Listen

Möglich, aber sehr gewöhnungsbedürftige Syntax
Schlüsselwort this taucht zweimal mit verschiedenen Bedeutungen auf
  • framework_projects/vorlesung/pages/loop-nested-list.hbs
    {
      "numbers": [
        [1,2,3,4,5,6],
        [2,4,6,8,10,12],
        [3,6,9,12,15,18],
        [4,8,12,16,20,24],
        [5,10,15,20,25,30]
      ]
    }
    ---
    <table border="1">
      {{#each page.numbers}}
        <tr>
          {{#each this}}
            <td>
              {{ this }}
            </td>
          {{/each}}
        </tr>
      {{/each}}
    </table>

Schleifen und der aktuelle Kontext

Technisches Detail: Daten sind in Handlebars in verschiedenen Kontexten organisiert
Gleiches Prinzip wie Blöcke in Programmiersprachen
Mit this kann auf den aktuellen Kontext zugegriffen werden
In jedem der bisherhigen Beispiele hätte man auch this.page.wert schreiben können
Schleifen (each) eröffnen einen neuen Kontext
Verzweigungen (if) hingegen nicht
Allerdings: Daten aus dem übergeordneten Kontext stehen nicht automatisch zur Verfügung
Abweichung zu Sprachen wie Visual Basic, Pascal, Java, …
Mit ../ kann auf den Kontext der übergeordneten Ebene zugegriffen werden
Lässt sich auch mehrfach angeben

Beispiel zum Zugriff auf übergeordneten Kontext

  • framework_projects/vorlesung/pages/loop-context.hbs
    {
      "name": "Hagrid",
      "tiere": [
        "Spinne", "Flubberwürmer", "Hippogreif",
        "Niffler", "Knallrümpfige Kröter"
      ]
    }
    ---
    <ul>
      {{#each page.tiere}}
        <li>{{../page.name}} stellte vor: {{ this }}</li>
      {{/each}}
    </ul>
    

Mehrzeilige SQL-Ergebnisse sind Listen mit Objekten

Praktisch: Verarbeitung von SQL-Abfragen funktioniert wie gewohnt
Einziges Problem: Unterscheidung zwischen ein- und mehrzeiligen Ergebnissen
  • framework_projects/vorlesung/pages/db-all-chapters.hbs
    chapter: >
      SELECT *
      FROM chapter;
    ---
    <ul>
      {{#each page.chapter}}
        <li>{{ this.caption }}</li>
      {{/each}}
    </ul>
    

Mehrzeilige SQL-Ergebnisse als Tabelle

  • framework_projects/vorlesung/pages/db-all-chapters-table.hbs
    chapter: >
      SELECT *
      FROM chapter;
    ---
    <table>
      <tr>
        <th>ID</th>
        <th>Überschrift</th>
      </tr>
      {{#each page.chapter}}
        <tr>
          <td>{{ this.chapter_id }}</td>
          <td>{{ this.caption }}</td>
        </tr>
      {{/each}}
    </table>
    

Verzweigungen mit dem if-Block

Allgemeine Syntax: Blöcke werden mit {{#name}} eingeleitet und mit {{/name}} geschlossen
  • #-Zeichen kennzeichnet einen öffnenden Block
  • name des Blocks muss beim Öffnen und Schließen identisch sein
  • Inhalt des Blocks unterliegt dann speziellen Regeln (je nach Typ des Blocks), typischerweise in Abhängigkeit von bereitgestellten Daten
Spezieller if-Block: Angabe eines Wahrheitswertes
  • Inhalt des Blocks wird nur ausgegeben, wenn die Bedingung erfüllt ist
  • Wahrheitswert darf (sollte?) aus den Daten kommen
Folgende Werte erfüllen eine Bedingung nicht (“falsy”):
  • false
  • 0
  • "" (leere Zeichenkette)
  • [] (leere Liste)
Alle anderen Werten gelten als wahr (“truthy”):
true, Strings mit Inhalt, Zahlen ≠ 0, …

Beispiel zum if-Block

  • framework_projects/vorlesung/pages/if-basics.hbs
    {
      "vorname": "Hermine",
      "nachname": "Granger",
      "weiblich": true,
      "geburtsjahr": 1979
    }
    ---
    <ul>
      <li>Vorname: {{ page.vorname }}</li>
      <li>Nachname: {{ page.nachname }}</li>
      {{#if page.weiblich}}
        <li>Geschlecht: Weiblich</li>
      {{/if }}
      {{#if page.männlich}}
        <li>Geschlecht: Männlich</li>
      {{/if}}
      <li>Geburtsjahr: {{ page.geburtsjahr }}</li>
    </ul>

Beispiel zum if-Block: Redundanz vermeiden

Angabe der Zeichenkette “Geschlecht” bisher zweimal notwendig
Durch Verschieben des if-Blocks nur einmalige Angabe notwendig
  • framework_projects/vorlesung/pages/if-basics-compact.hbs
    {
      "vorname": "Ronald Bilius",
      "nachname": "Weasley",
      "männlich": true,
      "geburtsjahr": 1980
    }
    ---
    <ul>
      <li>Vorname: {{ page.vorname }}</li>
      <li>Nachname: {{ page.nachname }}</li>
      <li>
        Geschlecht:
        {{#if page.weiblich}}
          Weiblich
        {{/if }}
        {{#if page.männlich}}
          Männlich
        {{/if}}
      </li>
      <li>Geburtsjahr: {{ page.geburtsjahr }}</li>
    </ul>

Beispiel zum if-Block: Doppelte Geschlechter

Potenzielles Problem: Unsaubere Daten, welche sich nicht gegenseitig ausschließen
Bei statischen Frontmatter-Daten leicht zu sehen, bei Datenbanken schon schwieriger
  • framework_projects/vorlesung/pages/if-basics-double.hbs
    {
      "vorname": "Kim",
      "nachname": "Sheringham",
      "weiblich": true,
      "männlich": true,   
      "geburtsjahr": 1979
    }
    ---
    <ul>
      <li>Vorname: {{ page.vorname }}</li>
      <li>Nachname: {{ page.nachname }}</li>
      <li>
        Geschlecht:
        {{#if page.weiblich}}
          Weiblich
        {{/if }}
        {{#if page.männlich}}
          Männlich
        {{/if}}
      </li>
      <li>Geburtsjahr: {{ page.geburtsjahr }}</li>
    </ul>

Alternativen mit dem else-Block

Zu jedem if-Block kann optional ein else-Block definiert werden
  • Wird ausgeführt wenn der if-Block eben nicht ausgeführt wurde
  • Es werden niemals beide Blöcke ausgeführt
  • framework_projects/vorlesung/pages/if-else.hbs
    {
      "vorname": "Firenze",
      "nachname": "(Zentaur)",
      "geburtsjahr": "unbekannt"
    }
    ---
    <ul>
      <li>Vorname: {{ page.vorname }}</li>
      <li>Nachname: {{ page.nachname }}</li>
      <li>
        Geschlecht: 
        {{#if page.weiblich}}
          Weiblich
        {{else}}
          Nicht weiblich (Männlich?)
        {{/if}}
      </li>
      <li>Geburtsjahr: {{ page.geburtsjahr }}</li>
    </ul>

(Leere) Listen mit Verzweigungen

  • framework_projects/vorlesung/pages/loop-conditional.hbs
    {
        "personen": [{
          "name": "Pippilotta Viktualia Rollgardina Pfefferminz Efraimstochter Langstrumpf",
          "hobbies": ["Pferde heben", "Limonade brauen", "reiten"]
        }, {
          "name": "Fräulein Prysselius",
          "hobbies": []
        }
      ]
    }
    ---
    {{#each page.personen}}
      <p>
        Mein Name ist {{ this.name }} und
        {{#if this.hobbies }}
          meine Hobbies sind
          <ul>
            {{#each this.hobbies }}
              <li>{{ this }}</li>
            {{/each}}
          </ul>
        {{ else }}
          ich habe keine Hobbies.
        {{/ if }}
      </p>
    {{/each}}

Benutzereingaben & URL-Parameter

Bisher: Jede Seite wird bei jedem Aufruf immer auf die exakt gleiche Art und Weise dargestellt
(Es sei denn, man verändert in der Zwischenzeit die Datenbank)
Neu: Benutzereingaben können die Seite verändern
  • Suchen nach bestimmten Werten
  • Anzeigen von bestimmten Datensätzen

URL Query Parameter

Jeder URL können mittels des Query-Teils (eingeleitet durch ?) beliebige Paramter übergeben werden
Es muss allerdings nicht jeder Parameter notwendigerweise auch verarbeitet werden
Parameterangabe in Form von name=wert-Paaren
  • /suche?begriff=mist
  • /kapitel?nummer=4
Jeder Parameter nach dem ersten wird durch ein & getrennt
  • /suche?begriff=mist&seit=2009
  • /kapitel?nummer=4&tageszeit=abend

Zugriff auf URL Query Parameter mit request.get

request-Objekt bietet auf jeder Seite Zugriff auf Benutzereingaben
GET steht für URL-Parameter
  • framework_projects/vorlesung/pages/url-parameter-get.hbs
    Hier sollte das Kapitel mit der ID {{ request.get.kapitel }} stehen.

Vorsicht: GET-Parameter müssen nicht zwingend vorhanden sein

Prüfung mit if ist sinnvoll
Filtert möglicherweise aber auch valide Werte mit raus
  • framework_projects/vorlesung/pages/url-parameter-optional.hbs
    {{# if request.get.kapitel }}
      Hier sollte das Kapitel mit der ID {{ request.get.kapitel }} stehen.
    {{ else }}
      Hey, es muss schon ein Kapitel angegeben werden!
    {{/ if }}
Frage:

Welche potenziell validen, aber von einem if als “falsch” deklarierten, Werte könnte ein Benutzer angeben?

Antwort:

Den leeren String und die Zahl 0

Zugriff auf URL Query Parameter in SQL-Abfragen

Zugriff auf URL-Parameter auch aus SQL möglich
Erlaubt die Parametriesierung der Abfragen
SQL-Syntax: :platzhalter, also ein Doppelpunkt gefolgt vom Namen des Platzhalters
Standard SQL-Syntax um Abfragen zu parametrisieren
Im fhw-web-Framework: Alle URL-Parameter stehen in jeder Abfrage zur Verfügung
Präfix request.get nicht nötig
  • framework_projects/vorlesung/pages/url-parameter-db.hbs
    chapter: >
      SELECT *
      FROM chapter
      WHERE chapter_id = :kapitel;
    ---
    Das Kapitel mit der ID {{ page.chapter.chapter_id }}
    heißt "{{ page.chapter.caption }}".
    

HTML-Benutzereingaben

Benutzereingaben im Web funktionieren über bekannte Bedienelemente
Vornehmlich Textfelder, allerdings mit zunehmender Spezialisierung

Das <form>-Element

Gruppiert Eingabeelemente die logisch zusammengehören
Alle aktiven Kinder-Eingabeelemente eines Formulars werden übertragen
Gibt Übertragungsmethode vor: method-Attribut
HTTP-Verben GET und POST mit unterschiedlicher Semantik
Gibt Übertragungsziel vor: action-Attribut
Angabe einer URL

Eingabeelemente

Typischstes Eingabelemenet ist <input type="text"> für Tastatureingaben
Kann anhand des type-Attributes für bestimmte Eingaben spezialisiert werden
Benötigtes Attribut: name
  • Unter diesem Namen wird der vom Benutzer eingetragene Wert an den Server verschickt
  • Wenn dieses Attribut fehlt, wird der Wert nicht übermittelt
Hilfreiches Attribut: value
  • Ändert sich durch Benutzereingaben
  • Kann zur Vergabe von Standardwerten genutzt werden

Abschicken von Formularen mit <input type="submit">

Knopf zum Abschicken des Formulars
Dargestellter Text kann mit dem value-Attribut beeinflusst werden
Nur der Wert des gedrückten Knopfes wird übertragen
Auch die anderen Knöpfe über einen Namen verfügen sollten, gilt nur einer dieser Knöpfe als aktiv

Formular mit method="GET"

Formular überträgt die Eingabeelemente innerhalb des Formulars als Teil der URL
Ziel wird durch action definiert und soll in diesem Fall die Werte einfach nur ausgeben
Problem: Das Eingabefeld für Kreditkartennummern hat keinen Namen
Wird daher nicht übertragen, wird aber dennoch angezeigt
Ziel /form: Nimmt beliebige Parameter entgegen und zeigt Sie in JSON-Darstellung an
  • Werte im query-Objekt stehen für GET-Parameter
  • Werte im body-Objekt stehen für POST-Parameter
  • framework_projects/vorlesung/pages/form-greet.hbs
    { "template": "html" }
    ---
    <form method="GET" action="/form">
      <p>
        Ihr Name:
        <input type="text" name="username">
      </p>
      <p>
        Ihre Kreditkartennummer:
        <input type="text">
      </p>
      <input type="submit" name="accept" value="Name setzen!">
      <input type="submit" name="decline" value="Bestimmt nicht!!">
    </form>
    

Versteckte Eingabefelder mit type="hidden"

Manche Parameter werden nicht vom Benutzer, sondern vom Programm vorgegeben
Und sollen trotzdem mit dem Formular übertragen werden
name und value müssen wie gehabt angegeben werden
Versteckte Formularfelder werden allerdings nicht dargestellt
  • framework_projects/vorlesung/pages/form-hidden.hbs
    { "template": "html" }
    ---
    <form method="GET" action="/form">
      <p>
        Ihr Name:
        <input type="text" name="username">
      </p>
      <input type="hidden" name="secret" value="2">
      <input type="submit" name="accept" value="Name übertragen">
    </form>