-
Notifications
You must be signed in to change notification settings - Fork 12
/
03-templates.md.erb
233 lines (159 loc) · 11 KB
/
03-templates.md.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
---
title: Templates
slug: templates
date: 0003/01/01
number: 3
points: 1
photoUrl: http://www.flickr.com/photos/73449134@N04/8194499092/
photoAuthor: Mike Lewinski
contents: Handlebars, Meteors Templating Sprache, kennenlernen|deine ersten drei Templates erstellen|erfahren wie Meteors Manager funktionieren|einen ersten Prototyp mit statischen Daten erstellen
paragraphs: 46
---
„“
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:
~~~html
<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>
~~~
<%= caption "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.
<% note do %>
### 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.
<% end %>
Wir sind nun so weit, unser zweites Template anzulegen. In `client/templates/posts`, erzeuge `posts_list.html`:
~~~html
<template name="postsList">
<div class="posts">
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>
~~~
<%= caption "client/templates/posts/posts_list.html" %>
und `post_item.html`:
~~~html
<template name="postItem">
<div class="post">
<div class="post-content">
<h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>
</div>
</div>
</template>
~~~
<%= caption "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}}`.
<% note do %>
### Weitere Informationen
Um mehr über Spacebars zu lernen, lies die [Spacebars-Documentation](https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md)
<% end %>
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.
<% note do %>
### 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.
<% end %>
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:
~~~js
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
});
~~~
<%= caption "client/templates/posts/posts_list.js" %>
Wenn man alles richtig gemacht hat, sollte das im Browser ungefähr so aussehen:
<%= screenshot "3-1", "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:
~~~html
<template name="postsList">
<div class="posts page">
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>
~~~
<%= caption "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 Array`postsData` 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:
~~~js
Template.postItem.helpers({
domain: function(){
var a = document.createElement('a');
a.href = this.url;
return a.hostname;
}
});
~~~
<%= caption "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).
<%= screenshot "3-2", "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` " %>
<% note do %>
### 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.
<% end %>
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!
<% note do %>
### 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!
<% end %>