-
Notifications
You must be signed in to change notification settings - Fork 39
/
03-templates.md.erb
228 lines (154 loc) · 16 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
---
title: Шаблоны
slug: templates
date: 0003/01/01
number: 3
contents: Узнаете о языке шаблонов Meteor, Spacebars.|Создадите свои первые 3 шаблона.|Узнаете как работают менеджеры Meteor.|Основы статического прототипирования.
paragraphs: 46
---
Чтобы облегчить разработку в Meteor, мы будем применять outside-in подход. Другими словами, в начале мы создадим обычный HTML/JavaScript шаблон, а затем подключим его к нашему приложению.
Это означает, что в этой главе мы коснемся только того, что происходит в директории `/client`.
Создадим новый файл `main.html` в директории `/client` и добавим в файл следующий код:
~~~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" class="row-fluid">
{{> postsList}}
</div>
</div>
</body>
~~~
<%= caption "client/main.html" %>
Это будет наш главный шаблон приложения. Как вы заметили, это обычный HTML, кроме тега `{{> postsList}}`. Он является точкой входа шаблона `postsList`, который мы создадим позже. Теперь создадим еще пару шаблонов.
### Шаблоны в Meteor
По своей сути, социально-новостной сайт состоит из сообщений, организованных в виде списка, именно так мы организуем наши шаблоны.
Теперь создадим директорию `/views` внутри `/client`. Туда мы положим все наши шаблоны. Также создадим директорию `/posts` внутри `/views`. Там будут находится шаблоны, которые связаны с новостями.
<% note do %>
### Поиск файлов
Meteor силен в поиске файлов. Не имеет значения, куда мы положили код внутри директории `/client`, Meteor найдет его и обработает должным образом. Это означает, что нет необходимости вручную прописывать пути к JavaScript и CSS файлам.
Другими словами, вы вполне можете хранить все ваши файлы в одном каталоге или же весь код в одном файле. Но так как Meteor в любом случае соберет все файлы в один, будет гораздо удобнее разбить все на части и держать код в чистоте, в виде хорошей и понятной структуры.
<% end %>
Итак, создадим наш второй шаблон. В директории `client/views/posts`, создадим файл `posts_list.html` со следующим содержанием:
~~~html
<template name="postsList">
<div class="posts">
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>
~~~
<%= caption "client/views/posts/posts_list.html" %>
Также создадим файл `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/views/posts/post_item.html" %>
Обратите внимание на атрибут `name="postsList"` тега `<template>` . Это имя используется для того, чтобы Meteor понимал, какой и где шаблон используется. В этом случае шаблон будет внутри основного (его мы создали ранее), вместо тега `{{> postsList}}`.
Настало время освоить шаблонизатор, который используется в Meteor - [Handlebars](http://handlebarsjs.com). Handlebars это простой HTML с добавлением трех вещей: *partials*, *expressions* и *block helpers*.
*Partials* - это конструкция вида `{{> имяШаблона}}`, с помощью которой мы обращаемся к Meteor и сообщаем ему, чтобы он заменил ее шаблоном с тем же именем (в нашем случае это `postItem`).
*Expressions*, такие как `{{title}}`, или вызывают свойство текущего объекта, или же возвращают значение, которое определено в текущем менеджере шаблонов (подробнее об этом позже).
*Block helpers* - это специальные теги, такие как `{{#each}}…{{/each}}` или `{{#if}}…{{/if}}`, с помощью которых можно создавать логические конструкции прямо в шаблоне.
<% note do %>
### Едем дальше
Вы можете зайти на [официальный сайт Handlebars](http://handlebarsjs.com) или на [этот удобный учебник](http://javascriptissexy.com/handlebars-js-tutorial-learn-everything-about-handlebars-js-javascript-templating/) если хотите узнать больше о Handlebars.
<% end %>
Вооружившись этими знаниями, мы можем легко понять, что здесь происходит.
Во-первых, шаблон `postsList` мы выводим в цикле с помощью конструкции `{{#each}}…{{/each}}`. Для каждой итерации мы подключаем шаблон `postItem`.
Так откуда берется объект `posts`? Хороший вопрос. На самом деле это template helper, мы определим его позже, после того, как посмотрим на template managers.
Шаблон `postItem` довольно прост. Он использует только три выражения: `{{url}}` и `{{title}}`, которые возвращают свойства документа, и `{{domain}}`, который вызывает template helper.
Мы уже не раз упоминали выражение "template helpers" в этой главе, не объясняя, что он делает. Но для того, чтобы исправить это, мы должны сначала поговорить о менеджерах.
### Template Managers (менеджеры шаблонов)
До сих пор мы имели дело с Handlebars, который является обычным HTML с дополнительными тегами. В отличие от других языков, таких как PHP (или даже обычных HTML страниц, которые могут содержать JavaScript), Meteor разделяет шаблоны от логики, и эти шаблоны ничего не делают сами по себе.
Для того, чтобы вдохнуть жизнь в шаблон, нам нужен **manager**. Вы можете думать о менеджере как о поваре, который получает сырье (ваши данные) и готовит их, прежде чем передать готовое блюдо официанту (шаблону), который принесет его вам.
Другими словами, в то время как роль шаблона ограничивается отображением или циклом с переменными, менеджер делает тяжелую работу по присвоению значения каждой переменной.
<% note do %>
### Менеджеры?
Когда мы спросили у разработчиков Meteor, что они подразумевают под фразой `template managers`, то кто-то из них сказал, что это контроллеры, а кто-то сказал: "Это те файлы, куда я положил свой JavaScript код".
Менеджеры на самом деле не являются контроллерами (по крайней мере, не в смысле контроллеров в MVC) и "Те файлы, куда я положил свой JavaScript код" тоже не является истиной, поэтому мы отклонили оба ответа.
Так как нам нужно было определиться с названием, мы придумали термин "менеджер" - подходящее слово, у которого нет множества других значений в веб-разработке.
<% end %>
Для простоты, мы будем называть файлы менеджеров шаблонов как и сами шаблоны, за исключением их расширения **.js**. Итак, создадим файл `posts_list.js` в директории `/client/views/posts` прямо сейчас и начнем создание нашего первого менеджера:
~~~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/views/posts/posts_list.js" %>
Если вы все сделали правильно, теперь вы должны видеть что-то похожее в вашем браузере:
<%= screenshot "3-1", "Our first templates with static data" %>
Мы делаем две вещи. Во-первых, устанавливаем макет прототипа данных в виде массива, обозначенного как `postsData`. Эти данные, как правило, берутся из базы данных, но так как мы пока не знаем, как работать с базой (потерпите до следующей главы), то мы временно будем использовать статические данные.
Во-вторых, мы используем функцию `Template.myTemplate.helpers()` для определения шаблона хелпера `posts`, который просто возвращает наш массив `postsData`.
Определение хелпера `posts` означает, что он теперь доступен для использования в нашем шаблоне:
~~~html
<template name="postsList">
<div class="posts">
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>
~~~
<%= caption "client/views/posts/posts_list.html" %>
Таким образом, наш шаблон может перебирать массив `postsData` и подставлять каждый элемента массива в цикле вместо тега `postItem` в шаблоне.
<%= commit "3-1", "Added basic posts list template and static data." %>
### Хелпер `domain`
Теперь создадим менеджер `post_item.js`, в котором будет храниться логика шаблона `postItem`:
~~~js
Template.postItem.helpers({
domain: function() {
var a = document.createElement('a');
a.href = this.url;
return a.hostname;
}
});
~~~
<%= caption "client/views/posts/post_item.js" %>
На этот раз наш хелпер `domain` является не массивом, а анонимной функцией. Этот паттерн является более распространенным (и более полезным) по сравнению с нашим предыдущим примером, где мы использовали массив вместо базы данных.
<%= screenshot "3-2", "Displaying domains for each links." %>
Хелпер `domain` берет URL и возвращает домен, используя немного магии JavaScript. Но где же брать url?
Ответ на вопрос мы можем найти, вернувшись немного назад, к файлу шаблона `posts_list.html`. Блок `{{#each}}` не только перебирает наш массив, а еще и **устанавливает значение `this` внутри блока**.
Это означает, что между тэгами `{{#each}}`, каждый пост во время итерации обозначается как `this`, и это распространяется на все пути, которые подключены внутри менеджера (`post_item.js`).
Теперь мы понимаем, почему `this.url` возвращает текущий URL поста (элемента массива postsData). Более того, если мы используем `{{title}}` и `{{url}}` внутри нашего шаблона `post_item.html`, Meteor понимает, что мы имеем в виду, написав `this.title` и `this.url`, и возвращает нам корректные значения.
<%= commit "3-2", "Setup a `domain` helper on the `postItem`." %>
<% note do %>
### Магия JavaScript
Хотя это не является специфичным для Meteor, но вот краткое объяснение того, что происходило выше: это всего лишь немного “магии JavaScript”. Во-первых, мы создаем пустой элемент HTML (`a`) и храним его в памяти.
Затем мы устанавливаем ему атрибут `href`, равный нашему URL (как вы заметили ранее, он является объектом хелпера и вызывается через `this`).
Наконец, мы воспользуемся свойством `hostname` элемента `a`, чтобы получить ссылку только на домен, без остальной части URL.
<% end %>
Если вы сделали все без ошибок, то вы должны увидеть в браузере список сообщений. Этот список является всего лишь статическими данными. Мы пока не используем все прекрасные возможности real-time, которые предоставляет нам Meteor. Мы покажем вам, как использовать эти возможности в следующей главе!
<% note do %>
### Горячее обновление кода
Вы могли заметить, что нет необходимости обновлять страницу в браузере после каждого внесения правок в код.
Это потому что Meteor отслеживает все файлы в директории проекта и автоматически обновляет страницу, если обнаружит изменения в каком-либо файле.
Горячее обновление кода в Meteor устроено довольно умно, и может даже сохранять состояние вашего приложения между двумя обновлениями!
<% end %>