template.md 9.81 KB
Newer Older
1
2
# $.template

3
`$(selector).template()` renders HTML templates with JavaScript mixed inside them.
4
5
6
7

Example:

```html
8
<template>Your platform is <%= navigator.userAgent %></template>
9
10
11
12
13
<script>
  $('body').template()
</script>
```

14
15
renders all `template` or `script[type="text/html"]` elements as
[lodash templates](https://lodash.com/docs/#template).
16
17
18
19
20
This displays `Your platform is ...` and shows the userAgent just below the script tag.

- Anything inside `<% ... %>` is executed as Javascript.
- Anything inside `<%= ... %>` is evaluated and rendered in-place.

S Anand's avatar
S Anand committed
21
22
23
24
25
26
Notes:

- `<template>` tag is not supported by Internet Explorer. Use
  `<script type="text/html">` for IE compatibility.
- `<template>` requires valid HTML. `<a <%= classes %>>` is invalid HTML since
  it contains `<` inside a tag. Use `<script type="text/html">` in such cases.
27

28
29
30

## $.template variables

31
Templates can access any global variable. You can pass additional variables
32
33
using as `.template({var1: value, var2: value, ...})`. For example:

34
<!-- render:html -->
35
```html
36
<template class="example">
37
38
39
  <% list.forEach(function(item) { %>
    <div><%= item %></div>
  <% }) %>
40
</template>
41
<script>
42
  $('.example').template({list: ['a', 'b', 'c']})
43
44
45
46
47
</script>
```

To re-render the template, run `.template(data)` again with different data.

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
Templates can also use these default variables:

- `obj`: the full data object passed to the template
- `$node`: the generating `<template>` element as a jQuery object
  - `$node.attr('class')` returns the class of the template
  - `$node.data('key')` returns `data-key` in `<template data-key="...">`
- `$data`: a shortcut for `$node.data()`.
  `$data.key` is the same as `$node.data('key')`

If you pass `obj`, `$node` or `$data` explicitly to `.template({...})`, it
overrides the default variables. The values you pass take priority.

You can pass any JSON object as a data attribute. In the example below,
`data-list` and `data-obj` are interpreted as JSON objects:

<!-- render:html -->
```html
<template class="datavar" data-list="[1,2,3]" data-obj="{x:1,y:[2,3]}">
  list = <%= JSON.stringify($data.list) %>,
  obj = <%= JSON.stringify($data.obj) %>,
</template>
<script>
  $('.datavar').template()
</script>
```

This is particularly useful with [external sources](#template-external-source).
For example, using an external [datavars.html](datavars.html) template:

<!-- render:html -->
```html
<template class="datavars" src="datavars.html" data-x="[1,2]"></template>
<template class="datavars" src="datavars.html" data-x="[3,4,5]"></template>
<script>
  $('.datavars').template()
</script>
```


87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
## $.template subtemplates

You can use sub-templates as follows:

<!-- render:html -->
```html
<template class="row" data-target="false">
  <li class="<%- classes %>"><a href="<%- link  %>"><%- text %></a></li>
</template>
<template class="main-template" data-template-item=".row">
  <ul>
    <%= item({classes: "active", link: '..', text: 'Parent'}) %>
    <%= item({classes: "", link: '.', text: 'Current'}) %>
  </ul>
</template>
<script>
  $('.main-template').template()
</script>
```

`data-target="false"` ensures that the template `.row` is not rendered.
(This is typically used by sub-templates.)

`data-template-item=".row"` creates a function `item()` inside `.main-template`.
111
112
Calling `item()` renders `.row` as a sub-template.

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

**Notes**:

- The sub-template `name` in `data-template-<name>` can only contain
  lowercase letters, numbers and underscore.
- Sub-templates may themselves depend on, and call, other sub-templates.
- Sub-templates require a [Promise polyfill](https://www.npmjs.com/package/es6-promise) for IE.

Sub-templates can be from an [external source](#template-external-source) as
well. For example, this sub-template is loaded from `heading.html`:

```html
<template class="tmpl-head" src="heading.html" data-target="false"></template>
<template class="main-template" data-template-header=".tmpl-head">
  <%= header({title: "Page title"}) %>
</template>
```

131

132
## $.template animation
133

134
135
136
137
138
139
140
141
142
When using `type="text/html"`, templates are re-rendered. To *update* existing
elements, use `data-engine="vdom"` instead. This only changes attributes or
elements that need change. This allows us to animate attributes via CSS.

You need to include [morphdom](https://github.com/patrick-steele-idem/morphdom)
for this to work.

For example, this shows a circle in SVG bouncing around smoothly.

143
<!-- render:html -->
144
145
146
147
```html
<style>
  circle { transition: all 1s ease; }
</style>
148
149
150
<script src="../../ui/morphdom/dist/morphdom-umd.min.js"></script>
<script type="text/html" data-engine="vdom" class="bouncing-ball">
  <svg width="500" height="50">
151
    <circle cx="<%= x %>" cy="<%= y %>" r="5" fill="red"></circle>
152
153
154
155
  </svg>
</script>
<script>
  setInterval(function() {
156
157
158
    var x = Math.random() * 500
    var y = Math.random() * 50
    $('.bouncing-ball').template({x: x, y: y})    // Update the template to animate
159
160
161
162
163
164
165
  }, 1000)
</script>
```

You can also specify a `data-engine` via an option. For example:

```js
166
$('.animate').template(data, {engine: 'vdom'})
167
168
169
170
171
172
```


## $.template targets

To re-use the template or render the same template on a different DOM node,
173
174
175
run `.template(data, {target: selector})`. This allows you to declare templates
once and apply them across the body. For example:

176
177
178
179
180
181
182
183
<!-- render:html -->
```html
<div class="panel1 bg-primary text-white px-3"></div>
<div class="panel2 bg-success text-white px-3"></div>
<script type="text/html" class="targeted">
  The same template is rendered in <%- heading %>
</script>
<script>
184
$('.targeted')
185
186
187
    .template({heading: 'panel 1'}, {target: '.panel1'})
    .template({heading: 'panel 2'}, {target: '.panel2'})
</script>
188
189
```

190
191
The target can also be specified via a `data-target=".panel1"` on the script
template. This is the same as specifying `{target: '.panel'}`. For example:
192
193

```html
194
195
<script class="chart" data-target=".panel1">...</script>
<script class="chart" data-target=".panel2">...</script>
196
197
198
199
200
```


## $.template append

201
202
To append instead of replacing, use `data-append="true"`. Every time `.template`
is called, it appends rather than replaces. For example:
203

204
205
206
207
208
209
<!-- render:html -->
```html
<script type="text/html" class="list" data-append="true">
  <li>New item #<%- n %> appended</li>
</script>
<script>
210
$('.list')
211
212
213
  .template({n: 1})
  .template({n: 2})
</script>
214
215
```

216
217
You can also specify this as `.template(data, {append: true})`. You can also
append to an [existing target](#template-targets). For example:
218

219
<!-- render:html -->
220
```html
221
<ul class="existing-list">
222
223
224
  <li>Existing item</li>
  <!-- Every time .template() is called, the result is added as a list item here -->
</ul>
225
<script>
226
$('.list')
227
228
229
    .template({n: 1}, {append: true, target: '.existing-list'})
    .template({n: 2}, {append: true, target: '.existing-list'})
</script>
230
231
232
```

## $.template external source
233

234
Template containers can have an `src=` attribute that loads the template from a file:
235

236
<!-- render:html -->
237
```html
238
<script type="text/html" src="template.html" class="source"></script>
239
<script>
240
  $('.source').template()
241
242
</script>
```
243

244
245
If the `src=` URL returns a HTTP error, the HTML *inside* the script is rendered
as a template. The template can use:
246
247

- all data passed by the `$().template()` function, and
248
- an [xhr](http://api.jquery.com/Types/#jqXHR) object - which has error details.
249
250
251

For example:

252
<!-- render:html -->
253
```html
254
255
<script type="text/html" src="missing.html" class="missing">
  Template returned HTTP error code: <%= xhr.status %>.
256
257
258
  Data is <%= data %>
</script>
<script>
259
  $('.missing').template({data: [1, 2, 3]})
260
261
262
</script>
```

263
264
## $.template selector

265
`$(...).template()` renders all `script[type="text/html"]` nodes in or under the
266
267
268
selected node. Use `data-selector=` attribute to change the selector. For
example:

269
<!-- render:html -->
270
```html
271
272
273
<section data-selector=".render">
  <script type="text/html" class="no-render">This will not render</script>
  <script type="text/html" class="render">This will render</script>
274
275
</section>
<script>
276
  $('section[data-selector]').template()
277
278
279
</script>
```

280
You can also use the `selector: ...` option. For example:
281

282
<!-- render:html -->
283
```html
284
285
286
<div class="selector-target"></div>
<script type="text/html" class="try no-render">This will not render</script>
<script type="text/html" class="try render">This will render</script>
287
<script>
288
  $('.try').template({}, {selector: '.render', target: '.selector-target'})
289
290
291
</script>
```

292

293
294
295
## $.template dispose

Deletes the output of a rendered template, use `$('...').template('dispose')`.
296
This removes the DOM elements last created by the template.
297
298
299
300
301
302
303

This is useful for deleting error messages or previous output that's no longer
relevant.

```js
  $.getJSON('data').then(function (data) {
    // When we get data, show the dashboard, dispose any previous error
304
305
    $('.dashboard').template({ data: data })
    $('.error').template('dispose')
306
307
  }).fail(function (xhr, error, msg) {
    // If there's an error, dispose any previous dashboard, show the error
308
309
    $('.dashboard').template('dispose')
    $('.error').template({ error: msg })
310
311
312
313
314
315
316
  })
```

When [appending](#template-append), this only deletes the last rendered
template. But this behavior may change.


317
318
## $.template events

319
320
When a template is rendered or disposed, `template` is fired on the source. Attributes:

321
322
- `templatedata`: the passed data argument (e.g. `{...}`, `'dispose'`)
- `target`: the target nodes rendered or disposed (if any), as a jQuery object
323
324
325

For example:

326
327
328
329
330
331
<!-- render:html -->
```html
<script type="text/html" class="event">
  <pre>Event e.templatedata = <span class="data">filled by event handler</span></pre>
</script>
<script>
332
$('.event')
333
  .on('template', function(e) {       // When the template is rendered,
334
    e.target.find('.data')            // find the .data class inside target nodes
335
      .html(JSON.stringify(e.templatedata)) // and enter the template data
336
  })
337
338
  .template({x: 1})                   // Trigger template AFTER .on('template')
</script>
339
```