Templates

3

Zu ...% übersetzt

In diesem Kapitel wirst du:

  • Handlebars, Meteors Templating Sprache, kennenlernen
  • deine ersten drei Templates erstellen
  • erfahren wie Meteors Manager funktionieren
  • einen ersten Prototyp mit statischen Daten erstellen
  • „“ Um den Einstieg in die Entwicklung mit Meteor zu erleichtern, verfolgen wir einen Outside-In-Ansatz. In anderen Worten: wir bauen zuerst eine „dumme“ äußere Schale aus HTML/JavaScript und verknüpfen sie dann später mit der inneren Logik unserer App.

    Das bedeutet, dass wir uns in diesem Kapitel nur mit dem beschäftigen werden, was in dem Verzeichnis /client vor sich geht.

    Wenn das noch nicht geschehen ist, dann erzeuge im Verzeichnis /client eine neue Datei mit dem Namen main.html und fülle sie mit dem folgenden Code:

    <head>
      <title>Microscope</title>
    </head>
    <body>
      <div class="container">
        <header class="navbar navbar-default" role="navigation">
          <div class="navbar-header">
            <a class="navbar-brand" href="/">Microscope</a>
          </div>
        </header>
        <div id="main">
          {{> postsList}}
        </div>
      </div>
    </body>
    
    client/main.html

    Dies wird unser Haupt-Template. Wie du siehst, ist es reines HTML bis auf ein einzelnes Tag, das das Template einführt: {{> postsList}}. Es dient als Einfügepunkt für das zukünftige Template postsList. Für den Moment werden wir einige weitere Templates erzeugen.

    Meteor Templates

    In ihrem Kern besteht eine Soziale News-Site aus Postings, die in Listen organisiert sind und genau so werden wir unsere Templates organisieren.

    Lass’ uns ein Verzeichnis /templates unterhalb von /client anlegen. Dort werden wir alle unsere Templates einfügen und um die Dinge schön geordnet zu halten, legen wir noch ein Verzeichnis /posts unterhalb von /templates an, um die Templates für Postings zu speichern.

    Dateien finden

    Meteor ist gut im Finden von Dateien. Unabhängig davon, wo im Verzeichnis /client sich dein Code befindet, wird Meteor ihn finden und korrekt kompilieren. Das heißt, dass du keine Include-Pfade für JavaScript- oder CSS-Dateien von Hand festlegen brauchst.

    Es heißt auch, dass du genauso gut alle Dateien in das selbe Verzeichnis legen könntest oder sogar den ganzen Code in eine einzige Datei. Weil Meteor jedoch ohnehin alles in eine einzige minifizierte Datei kompiliert, halten wir die Dinge lieber gut organisiert und verwenden eine saubere Dateistruktur.

    Wir sind nun so weit, unser zweites Template anzulegen. In client/templates/posts, erzeuge posts_list.html:

    <template name="postsList">
      <div class="posts">
        {{#each posts}}
          {{> postItem}}
        {{/each}}
      </div>
    </template>
    
    client/templates/posts/posts_list.html

    und post_item.html:

    <template name="postItem">
      <div class="post">
        <div class="post-content">
          <h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>
        </div>
      </div>
    </template>
    
    client/templates/posts/post_item.html

    Beachte das Attribut name="postsList" des Elementes template. Das ist der Name, der von Meteor verwendet wird, um nachzuverfolgen, welches Template wo eingesetzt ist (beachte, dass der eigentliche Dateiname keine Bedeutung hat).

    Nun ist es an der Zeit, Meteors Template-System Spacebars vorzustellen. Spacebars besteht aus HTML mit drei zusätzlichen Dingen: inclusions (manchmal auch „partials“ genannt), expressions (Ausdrücke) und block helpers.

    Inclusions verwenden die Syntax {{> templateName}} und teilen Meteor lediglich mit, dass die inclusion durch das Template gleichen Namens ersetzt werden soll (in unserem Fall postItem).

    Expressions, wie {{title}} rufen entweder eine property des aktuellen Objekts oder den Rückgabewert eines Template-Helpers, wie er in dem aktuellen Template-Manager definiert ist (dazu später mehr).

    Schließlich sind block helpers besondere Tags, die den Ablauf innerhalb des Templates steuern, wie {{#each}}…{{/each}} oder {{#if}}…{{/if}}.

    Weitere Informationen

    Um mehr über Spacebars zu lernen, lies die Spacebars-Documentation

    Mit diesem Wissen konnen wir anfangen, zu verstehen, was hier passiert.

    Zuerst iterieren wir im Template postsLists mittels des Block-Helpers {{#each}}…{{/each}} über ein Objekt posts. Dann fügen wir für jede Iteration das Template postItem ein.

    Woher kommt dieses Objekt posts? Gute Frage. Eigentlich ist es ein Template-Helper und du kannst es als einen Platzhalter für einen dynamischen Wert ansehen.

    Das Template postItem selbst ist ziemlich einfach: Es benutzt lediglich drei Expressions: {{url}} und {{title}}liefern Properties des Dokuments und {{domain}} ruft einen Template-Helper auf.

    Template-Helper

    Bis jetzt haben wir uns mit Spacebars beschäftigt, was kaum mehr als HTML mit ein paar zusätzlichen Tags ist. Anders als andere Sprachen wie PHP (oder sogar gewöhnliche HTML-Seiten, die JavaScript einbinden können), separiert Meteor Templates und deren Logik, so dass Templates selbst nicht viel machen.

    Um zum Leben erweckt zu werden, braucht ein Template helper. Man kann sich diese Helper wie Köche vorstellen, die rohe Zutaten (deine Daten) nehmen und verarbeiten, bevor sie das fertige Gericht (das Template) and den Kellner übergeben, der es dir präsentiert.

    In anderen Worten: während die Aufgabe des Templates darauf beschränkt ist, Variablen anzuzeigen oder darüber zu iterieren, sind es die Helper, die den Hauptteil der Arbeit erledigen indem sie jeder Variable einen Wert zuweisen.

    Controller?

    Man könnte meinen, dass die Datei mit den Template-Helpern eine Art Controller darstellt. Doch dies ist missverständlich (zumindest im Sinne von MVC), da Controller sonst eine etwas andere Rolle spielen.

    Wir haben uns deshalb entschieden, diesen Begriff zu vermeiden und einfach auf “Template-Helper” oder “Template-Logik” zu verweisen, wenn es um den, mit Templates verknüpften, JavaScript Code geht.

    Um die Dinge zu vereinfachen, übernehmen wir die Konvention und benennen die Datei mit den Helpern nach dem zugehörigen Template, aber mit einer .js Endung. Also erstellen wir jetzt die Datei posts_list.js im Verzeichnis client/templates/posts und beginnen darin unseren ersten Helper zu erstellen:

    var postsData = [
      {
        title: 'Introducing Telescope',
        url: 'http://sachagreif.com/introducing-telescope'
      },
      {
        title: 'Meteor',
        url: 'http://meteor.com'
      },
      {
        title: 'The Meteor Book',
        url: 'http://themeteorbook.com'
      }
    ];
    Template.postsList.helpers({
      posts: postsData
    });
    
    client/templates/posts/posts_list.js

    Wenn man alles richtig gemacht hat, sollte das im Browser ungefähr so aussehen:

    Unser erstes Template mit statischen Daten
    Unser erstes Template mit statischen Daten

    Wir machen hier zwei Dinge: zuerst schreiben wir Beispieldaten in das Array postsData. Diese Daten würden normalerweise aus einer Datenbank kommen, aber da wir noch nicht gelernt haben wie das funktioniert (kommt im nächsten Kapitel!), bedienen wir uns dieses “Tricks” und benutzen statische Daten.

    Dann benutzen wir Meteors Funktion Template.postsList.helpers() um einen Template-Helper namens posts zu erstellen, der das Array postsData zurückgibt, dasss wir gerade definiert haben.

    Wie du dich vielleicht erinnern kannst, benutzen wir den Helper posts bereits in unserem postsList Template:

    <template name="postsList">
      <div class="posts page">
        {{#each posts}}
          {{> postItem}}
        {{/each}}
      </div>
    </template>
    
    client/templates/posts/posts_list.html

    Da wir den Helper postsList definiert haben, steht er nun im Template zur Verfügung, wo wir nun über unser ArraypostsData iterieren und jedes Objekt, das darin enthalten ist, an das Template postItem übergeben lassen können.

    Commit 3-1

    Added basic posts list template and static data

    Der domain Helper

    Ähnlich wie zuvor, erstellen wir nun die Datei post_item.js, die die Logik des Templates postItem enthalten soll:

    Template.postItem.helpers({
      domain: function(){
        var a = document.createElement('a');
        a.href = this.url;
        return a.hostname;
      }
    
    });
    
    client/templates/posts/post_item.js

    Dieses mal ist der Wert des Helpers domain kein Array, sondern eine anonyme Funktion. Im Vergleich zu unserem vorigen Beispiel mit den statischen Beispieldaten, ist dieses Muster viel gebräuchlicher (und nützlicher).

    Domains für jeden Link darstellen
    Domains für jeden Link darstellen

    Durch ein bisschen JavaScript Magie, nimmt der Helper domain eine URL und gibt deren Domain zurück. Aber woher nimmt er diese URL überhaupt?

    Um diese Frage zu beantworten, müssen wir uns noch einmal unser Template posts_list.html ansehen. Der Block-Helper {{#each}} iteriert nicht nur über unser Array, sondern er setzt auch den Wert von this inerhalb des Blocks auf das aktuell zurückgegebene Objekt.

    Das bedeutet, dass zwischen beiden {{#each}} Tags, jeder Post nacheinander this zugewiesen wird und das setzt sich auch in der inkludierten Template-Logik post_item.js fort.

    Jetzt verstehen wir warum this.url, die URL des aktuellen Posts zurückgibt. Desweiteren weiß Meteor, dass wenn wir {{title}} oder {{url}} in unserem Template post_item.html benutzen, wir eigentlich this.title und this.url meinen und gibt so die korrekten Werte zurück.

    Commit 3-2

    Setup a `domain` helper on the `postItem`

    JavaScript Magic

    Obwohl nicht Meteor-spezifisch, findest du hier eine kurze Erklärung der oben stehenden “JavaScript Magie”. Zuerst erstellen wir einen leeres HTML-Link Element (a) und speichern es in einer Variable.

    Dann setzen wir das href Attribut gleich der URL des aktuellen Posts. (wie wir gerade gesehen haben, repräsentiert this in einem Helper das Objekt, mit dem gerade gearbeitet wird).

    Zum Schluss machen wir uns die spezielle Eigenschaft hostname des a Elements zu nutzen und erhalten so die Domain ohne den Rest der URL.

    Wenn man bis hier korrekt mitgearbeitet hat, sollte man eine Liste von Posts im Browser angezeigt bekommen. Diese Liste ist statisch und nutzt noch keine von Meteors echtzeit Features. Wie du das ändern kannst, zeigen wir dir im nächsten Kapitel!

    Hot Code Reload

    Vielleicht ist dir schon aufgefallen, dass du deine Seite im Browser nicht manuell neu laden musst, nachdem du eine Änderung an der Datei vorgenommen hast.

    Das liegt daran, dass Meteor ständig alle Dateien im Projekt-Verzeichnis überwacht und automatisch den Browser neu läd, sobald eine Datei verändert wird.

    Meteors hot code reload ist ziemlich intelligent, so dass es sogar den Zustand deiner App zwischen zwei Aktualisierungen der Seite aufrecht erhält!