miki-template 2.3.1 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +24 -2
- package/src/codegen.js +140 -9
- package/src/parser.js +69 -1
- package/src/tags/control.js +112 -51
- package/src/tags/extra.js +4 -17
- package/src/tags/i18n.js +6 -17
- package/src/tags/inheritance.js +6 -20
- package/src/tags/util.js +5 -20
- package/.eslintrc.json +0 -16
- package/.github/release-notes/v1.3.1.md +0 -55
- package/.github/release-notes/v1.3.3.md +0 -77
- package/.github/release-notes/v1.3.7.md +0 -20
- package/.github/workflows/ci.yml +0 -42
- package/.github/workflows/docs.yml +0 -107
- package/.github/workflows/release.yml +0 -107
- package/AGENT.md +0 -71
- package/API_REFERENCE.md +0 -341
- package/CHANGELOG.md +0 -173
- package/CODE_OF_CONDUCT.md +0 -14
- package/CONTRIBUTING.md +0 -27
- package/ROADMAP.md +0 -40
- package/assets/banner.png +0 -0
- package/assets/logo.png +0 -0
- package/benchmarks/ejs-results.json +0 -17
- package/benchmarks/ejs.js +0 -38
- package/benchmarks/handlebars-results.json +0 -17
- package/benchmarks/handlebars.js +0 -45
- package/benchmarks/miki-results.json +0 -17
- package/benchmarks/miki.js +0 -39
- package/benchmarks/pug-results.json +0 -17
- package/benchmarks/pug.js +0 -38
- package/benchmarks/report.json +0 -17
- package/benchmarks/run.js +0 -81
- package/benchmarks/stress.mjs +0 -647
- package/benchmarks/templates/large.dtpl +0 -7
- package/benchmarks/templates/medium.dtpl +0 -3
- package/benchmarks/templates/small.dtpl +0 -7
- package/context/component.md +0 -109
- package/context/prd.md +0 -131
- package/context/project-structure.md +0 -33
- package/dir/base.html +0 -23
- package/dir/cmpnt.html +0 -11
- package/dir/footer.html +0 -3
- package/dir/home.html +0 -80
- package/dir/index.html +0 -80
- package/dir/navbar.html +0 -9
- package/docs/api/async-render.md +0 -170
- package/docs/api/cache.md +0 -174
- package/docs/api/compile.md +0 -256
- package/docs/api/context-processors.md +0 -154
- package/docs/api/filters.md +0 -437
- package/docs/api/finder.md +0 -188
- package/docs/api/helpers.md +0 -106
- package/docs/api/i18n.md +0 -314
- package/docs/api/index.md +0 -108
- package/docs/api/libraries.md +0 -414
- package/docs/api/render-partial.md +0 -162
- package/docs/api/render.md +0 -184
- package/docs/api/security.md +0 -290
- package/docs/api/setup-express.md +0 -152
- package/docs/api/tags.md +0 -268
- package/docs/assets/banner.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/filter.md +0 -0
- package/docs/guide/advanced-usage.md +0 -794
- package/docs/guide/async-rendering.md +0 -616
- package/docs/guide/context-processors.md +0 -514
- package/docs/guide/custom-filters.md +0 -622
- package/docs/guide/custom-tags.md +0 -542
- package/docs/guide/filters.md +0 -1314
- package/docs/guide/getting-started.md +0 -204
- package/docs/guide/installation.md +0 -190
- package/docs/guide/partial-templates.md +0 -734
- package/docs/guide/quick-start.md +0 -444
- package/docs/guide/security.md +0 -690
- package/docs/guide/tags.md +0 -1566
- package/docs/guide/template-discovery.md +0 -340
- package/docs/guide/template-inheritance.md +0 -546
- package/docs/guide/what-is-miki-template.md +0 -28
- package/docs/guide/why-miki-template.md +0 -75
- package/docs/index.md +0 -86
- package/docs/integrations/elysia.md +0 -80
- package/docs/integrations/express.md +0 -219
- package/docs/integrations/fastify.md +0 -79
- package/docs/integrations/hono.md +0 -80
- package/docs/integrations/index.md +0 -68
- package/docs/integrations/koa.md +0 -90
- package/docs/integrations/nestjs.md +0 -80
- package/docs/integrations/tsed.md +0 -83
- package/docs/performance.md +0 -74
- package/eslint.config.mjs +0 -42
- package/ex.mjs +0 -36
- package/live-test/integrations/elysia-example.js +0 -16
- package/live-test/integrations/express-example.js +0 -24
- package/live-test/integrations/fastify-example.js +0 -20
- package/live-test/integrations/hono-example.js +0 -16
- package/live-test/integrations/koa-example.js +0 -30
- package/live-test/integrations/nestjs-example.js +0 -25
- package/live-test/integrations/smoke-test.js +0 -166
- package/live-test/integrations/tsed-example.js +0 -23
- package/live-test/package-lock.json +0 -1150
- package/live-test/package.json +0 -13
- package/live-test/packages/product/templates/product/detail.html +0 -7
- package/live-test/server.js +0 -38
- package/live-test/templates/app_templates/detail.html +0 -6
- package/live-test/views/base.html +0 -8
- package/live-test/views/child.html +0 -7
- package/live-test/views/home.html +0 -17
- package/live-test/views/index.html +0 -1
- package/miki-template-extension/.github/workflows/ci.yml +0 -116
- package/miki-template-extension/.vscodeignore +0 -7
- package/miki-template-extension/CHANGELOG.md +0 -99
- package/miki-template-extension/LICENSE +0 -21
- package/miki-template-extension/README.md +0 -273
- package/miki-template-extension/extension.js +0 -1013
- package/miki-template-extension/icon.png +0 -0
- package/miki-template-extension/icon.svg +0 -10
- package/miki-template-extension/miki-template-1.7.1.vsix +0 -0
- package/miki-template-extension/package.json +0 -280
- package/miki-template-extension/snippets/miki-template.json +0 -717
- package/miki-template-extension/syntaxes/language-configuration.json +0 -114
- package/miki-template-extension/syntaxes/miki-template.tmLanguage.json +0 -355
- package/miki-template-extension/tests/grammar-tests.json +0 -162
- package/miki-template-extension/tests/run-grammar-tests.js +0 -82
- package/mkdocs.yml +0 -195
- package/overrides/main.html +0 -26
- package/requirements-docs.txt +0 -2
- package/sample-app/package-lock.json +0 -901
- package/sample-app/package.json +0 -9
- package/sample-app/server.js +0 -14
- package/sample-app/views/index.html +0 -1
- package/scripts/build-vsix.js +0 -129
- package/scripts/build-vsix.ps1 +0 -15
- package/snippets/miki-template.json +0 -177
- package/src/asyncRender.js +0 -21
- package/src/types.d.ts +0 -107
- package/syntaxes/language-configuration.json +0 -26
- package/syntaxes/miki-template.tmLanguage.json +0 -146
- package/tests/asyncRender.test.js +0 -17
- package/tests/base.html +0 -6
- package/tests/child.html +0 -3
- package/tests/context_processors.test.js +0 -13
- package/tests/esm.test.mjs +0 -61
- package/tests/filters.test.js +0 -263
- package/tests/finder-appdirs.test.js +0 -19
- package/tests/finder.test.js +0 -17
- package/tests/fixtures/views/nested/index.html +0 -1
- package/tests/fixtures/views/partial.html +0 -1
- package/tests/fixtures/views/sub/deepfile.html +0 -1
- package/tests/fixtures/views-appdirs/product/site/detail.html +0 -1
- package/tests/include_security.test.js +0 -9
- package/tests/integration/README.md +0 -32
- package/tests/integration/features.test.cjs +0 -1681
- package/tests/integration/features.test.mjs +0 -1697
- package/tests/integration/finder.esm.test.mjs +0 -13
- package/tests/integration/partial-render.test.cjs +0 -13
- package/tests/integration/templates/base.miki +0 -6
- package/tests/integration/templates/child.miki +0 -6
- package/tests/integration/templates/index.html +0 -17
- package/tests/lexer.test.js +0 -45
- package/tests/parser.test.js +0 -57
- package/tests/partial.html +0 -1
- package/tests/partialdef.test.js +0 -79
- package/tests/production_checks.js +0 -57
- package/tests/security.test.js +0 -28
- package/tests/tags.test.js +0 -233
package/docs/guide/tags.md
DELETED
|
@@ -1,1566 +0,0 @@
|
|
|
1
|
-
# Tags
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Tags control template logic and structure. They use `{% %}` syntax.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Table of Contents
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- [Control Flow](#control-flow)
|
|
14
|
-
|
|
15
|
-
- [Variable Assignment](#variable-assignment)
|
|
16
|
-
|
|
17
|
-
- [Change Detection](#change-detection)
|
|
18
|
-
|
|
19
|
-
- [Date and Time](#date-and-time)
|
|
20
|
-
|
|
21
|
-
- [Utility Tags](#utility-tags)
|
|
22
|
-
|
|
23
|
-
- [Security Tags](#security-tags)
|
|
24
|
-
|
|
25
|
-
- [Comments and Raw Output](#comments-and-raw-output)
|
|
26
|
-
|
|
27
|
-
- [Autoescape](#autoescape)
|
|
28
|
-
|
|
29
|
-
- [Library Loading](#library-loading)
|
|
30
|
-
|
|
31
|
-
- [Template Tags](#template-tags)
|
|
32
|
-
|
|
33
|
-
- [Inheritance Tags](#inheritance-tags)
|
|
34
|
-
|
|
35
|
-
- [Partial Tags](#partial-tags)
|
|
36
|
-
|
|
37
|
-
- [i18n Tags](#i18n-tags)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
## Control Flow
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
### if / elif / else / endif
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Conditional rendering with a wide range of operators.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
=== "Basic if"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```html
|
|
62
|
-
|
|
63
|
-
{% if user.is_authenticated %}
|
|
64
|
-
|
|
65
|
-
<p>Welcome back, {{ user.name }}!</p>
|
|
66
|
-
|
|
67
|
-
{% else %}
|
|
68
|
-
|
|
69
|
-
<p>Please <a href="/login">log in</a>.</p>
|
|
70
|
-
|
|
71
|
-
{% endif %}
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
=== "Multiple conditions with elif"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
```html
|
|
82
|
-
|
|
83
|
-
{% if user.role == 'admin' %}
|
|
84
|
-
|
|
85
|
-
<p>Admin panel</p>
|
|
86
|
-
|
|
87
|
-
{% elif user.is_staff %}
|
|
88
|
-
|
|
89
|
-
<p>Staff dashboard</p>
|
|
90
|
-
|
|
91
|
-
{% else %}
|
|
92
|
-
|
|
93
|
-
<p>Guest view</p>
|
|
94
|
-
|
|
95
|
-
{% endif %}
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
=== "Combined conditions with parentheses"
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
```html
|
|
106
|
-
|
|
107
|
-
{% if (user.role == 'admin' or user.is_staff) and user.is_active %}
|
|
108
|
-
|
|
109
|
-
<p>Active staff member</p>
|
|
110
|
-
|
|
111
|
-
{% endif %}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
{% if item not in cart_items %}
|
|
116
|
-
|
|
117
|
-
<button>Add to cart</button>
|
|
118
|
-
|
|
119
|
-
{% endif %}
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
**Supported operators:** `==`, `!=`, `<`, `<=`, `>`, `>=`, `in`, `not in`, `and`, `or`, `not`
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
**Operator precedence** (highest to lowest): comparison → `and` → `or`
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
### for / empty / endfor
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
Loop over arrays and objects. Injects `forloop` meta tracking.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
=== "Basic loop"
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
```html
|
|
146
|
-
|
|
147
|
-
<ul>
|
|
148
|
-
|
|
149
|
-
{% for item in items %}
|
|
150
|
-
|
|
151
|
-
<li>{{ forloop.counter }}: {{ item }}</li>
|
|
152
|
-
|
|
153
|
-
{% empty %}
|
|
154
|
-
|
|
155
|
-
<li>No items found</li>
|
|
156
|
-
|
|
157
|
-
{% endfor %}
|
|
158
|
-
|
|
159
|
-
</ul>
|
|
160
|
-
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
=== "Loop with filters"
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
```html
|
|
170
|
-
|
|
171
|
-
{% for group in items|regroup:"category" %}
|
|
172
|
-
|
|
173
|
-
<h3>{{ group.grouper }}</h3>
|
|
174
|
-
|
|
175
|
-
{% for item in group.list %}- {{ item.name }}
|
|
176
|
-
|
|
177
|
-
{% endfor %}
|
|
178
|
-
|
|
179
|
-
{% endfor %}
|
|
180
|
-
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
=== "Dictionary iteration"
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
```html
|
|
190
|
-
|
|
191
|
-
{% for key, value in config %}
|
|
192
|
-
|
|
193
|
-
<dt>{{ key }}</dt>
|
|
194
|
-
|
|
195
|
-
<dd>{{ value }}</dd>
|
|
196
|
-
|
|
197
|
-
{% endfor %}
|
|
198
|
-
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
=== "Nested loops"
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
```html
|
|
208
|
-
|
|
209
|
-
{% for department in departments %}
|
|
210
|
-
|
|
211
|
-
<h2>{{ department.name }}</h2>
|
|
212
|
-
|
|
213
|
-
{% for employee in department.employees %}
|
|
214
|
-
|
|
215
|
-
<span>#{{ forloop.parentloop.counter }}.{{ forloop.counter }} {{ employee.name }}</span>
|
|
216
|
-
|
|
217
|
-
{% endfor %}
|
|
218
|
-
|
|
219
|
-
{% endfor %}
|
|
220
|
-
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
#### forloop Metadata
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
| Variable | Description |
|
|
230
|
-
|
|
231
|
-
|----------|-------------|
|
|
232
|
-
|
|
233
|
-
| `forloop.counter` | 1-based index |
|
|
234
|
-
|
|
235
|
-
| `forloop.counter0` | 0-based index |
|
|
236
|
-
|
|
237
|
-
| `forloop.revcounter` | Reverse 1-based index |
|
|
238
|
-
|
|
239
|
-
| `forloop.revcounter0` | Reverse 0-based index |
|
|
240
|
-
|
|
241
|
-
| `forloop.first` | `true` on first iteration |
|
|
242
|
-
|
|
243
|
-
| `forloop.last` | `true` on last iteration |
|
|
244
|
-
|
|
245
|
-
| `forloop.parentloop` | Parent loop context (nested loops) |
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
**Real-world table with alternating row classes:**
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
```html
|
|
254
|
-
|
|
255
|
-
<table>
|
|
256
|
-
|
|
257
|
-
{% for row in rows %}
|
|
258
|
-
|
|
259
|
-
<tr class="{% cycle 'row-odd' 'row-even' %}">
|
|
260
|
-
|
|
261
|
-
<td>{{ row.name }}</td>
|
|
262
|
-
|
|
263
|
-
<td>{{ row.value }}</td>
|
|
264
|
-
|
|
265
|
-
</tr>
|
|
266
|
-
|
|
267
|
-
{% endfor %}
|
|
268
|
-
|
|
269
|
-
</table>
|
|
270
|
-
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
### with / endwith
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
Scope localized variables.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
=== "Alias a variable"
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
```html
|
|
288
|
-
|
|
289
|
-
{% with user.profile.address as addr %}
|
|
290
|
-
|
|
291
|
-
<p>{{ addr.city }}, {{ addr.zip }}</p>
|
|
292
|
-
|
|
293
|
-
{% endwith %}
|
|
294
|
-
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
=== "Multiple assignments"
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
```html
|
|
304
|
-
|
|
305
|
-
{% with greeting="Hello", who="World" %}
|
|
306
|
-
|
|
307
|
-
<p>{{ greeting }} {{ who }}</p>
|
|
308
|
-
|
|
309
|
-
{% endwith %}
|
|
310
|
-
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
=== "Combined alias"
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
```html
|
|
320
|
-
|
|
321
|
-
{% with a=5, b=10 as total %}
|
|
322
|
-
|
|
323
|
-
<p>Total: {{ total }}</p>
|
|
324
|
-
|
|
325
|
-
{% endwith %}
|
|
326
|
-
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
### cycle
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
Cycle through values sequentially.
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
=== "Alternating CSS classes"
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
```html
|
|
344
|
-
|
|
345
|
-
{% for row in rows %}
|
|
346
|
-
|
|
347
|
-
<tr class="{% cycle 'row-odd' 'row-even' %}">...</tr>
|
|
348
|
-
|
|
349
|
-
{% endfor %}
|
|
350
|
-
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
=== "Store without output (as)"
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
```html
|
|
360
|
-
|
|
361
|
-
{% cycle 'row-odd' 'row-even' as row_class %}
|
|
362
|
-
|
|
363
|
-
<tr class="{{ row_class }}">
|
|
364
|
-
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
=== "Named cycle for resumable state"
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
```html
|
|
374
|
-
|
|
375
|
-
{% for item in items %}
|
|
376
|
-
|
|
377
|
-
{% cycle 'a' 'b' 'c' as marker silent %}
|
|
378
|
-
|
|
379
|
-
{% if marker == 'b' %}
|
|
380
|
-
|
|
381
|
-
<strong>{{ item }}</strong>
|
|
382
|
-
|
|
383
|
-
{% else %}
|
|
384
|
-
|
|
385
|
-
{{ item }}
|
|
386
|
-
|
|
387
|
-
{% endif %}
|
|
388
|
-
|
|
389
|
-
{% endfor %}
|
|
390
|
-
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
### firstof
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
Return the first truthy value.
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
```html
|
|
404
|
-
|
|
405
|
-
{% firstof user.display_name user.username "Anonymous" %}
|
|
406
|
-
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
---
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
## Variable Assignment
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
### set
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
Assign a value to a variable.
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
=== "Inline assignment"
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
```html
|
|
432
|
-
|
|
433
|
-
{% set total = price * quantity %}
|
|
434
|
-
|
|
435
|
-
<p>Total: ${{ total|floatformat:2 }}</p>
|
|
436
|
-
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
=== "Block form (captures rendered output)"
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
```html
|
|
446
|
-
|
|
447
|
-
{% set greeting %}
|
|
448
|
-
|
|
449
|
-
Hello {{ user.name|title }}, welcome to {{ site.name }}!
|
|
450
|
-
|
|
451
|
-
{% endset %}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
<h1>{{ greeting|safe }}</h1>
|
|
456
|
-
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
=== "Multiple variables"
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
```html
|
|
466
|
-
|
|
467
|
-
{% set tax_rate = 0.08, tax = subtotal|mult:tax_rate %}
|
|
468
|
-
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
Variables set with `{% set %}` persist in the current scope and can be used after the tag.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
---
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
## Change Detection
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
### ifchanged / endifchanged
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
Render the body only when a value changes.
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
=== "Basic (no argument)"
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
```html
|
|
498
|
-
|
|
499
|
-
{% for item in changelog %}
|
|
500
|
-
|
|
501
|
-
{% ifchanged item.timestamp %}
|
|
502
|
-
|
|
503
|
-
<h3>{{ item.timestamp|date:"Y-m-d" }}</h3>
|
|
504
|
-
|
|
505
|
-
{% endifchanged %}
|
|
506
|
-
|
|
507
|
-
<p>{{ item.change }}</p>
|
|
508
|
-
|
|
509
|
-
{% endfor %}
|
|
510
|
-
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
=== "With else"
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
```html
|
|
520
|
-
|
|
521
|
-
{% for item in items %}
|
|
522
|
-
|
|
523
|
-
{% ifchanged item.category %}
|
|
524
|
-
|
|
525
|
-
<h2>{{ item.category }}</h2>
|
|
526
|
-
|
|
527
|
-
{% else %}
|
|
528
|
-
|
|
529
|
-
<p>Same category as above</p>
|
|
530
|
-
|
|
531
|
-
{% endifchanged %}
|
|
532
|
-
|
|
533
|
-
{% endfor %}
|
|
534
|
-
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
---
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
## Date and Time
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
### now
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
Output the current date/time.
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
```html
|
|
556
|
-
|
|
557
|
-
<p>Current time: {% now "Y-m-d H:i:s" %}</p>
|
|
558
|
-
|
|
559
|
-
<p>Pretty date: {% now "F j, Y" %}</p>
|
|
560
|
-
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
Uses the same format codes as the `date` filter (Django-style tokens like `Y`, `m`, `d`, `H`, `i`, `s`, `F`).
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
**Real-world copyright footer:**
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
```html
|
|
574
|
-
|
|
575
|
-
<footer>
|
|
576
|
-
|
|
577
|
-
© {{ "now"|date:"Y" }} {{ site.name }}. All rights reserved.
|
|
578
|
-
|
|
579
|
-
</footer>
|
|
580
|
-
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
---
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
## Utility Tags
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
### static
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
Generate a static file URL.
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
```html
|
|
602
|
-
|
|
603
|
-
<link rel="stylesheet" href="{% static "css/main.css" %}">
|
|
604
|
-
|
|
605
|
-
<script src="{% static "js/app.js" %}"></script>
|
|
606
|
-
|
|
607
|
-
<img src="{% static "images/logo.svg" }}" alt="{{ site.name }}">
|
|
608
|
-
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
Configure the prefix at compile time:
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
=== "CommonJS"
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
```javascript
|
|
622
|
-
|
|
623
|
-
const { compile } = require('miki-template');
|
|
624
|
-
|
|
625
|
-
const template = compile(source, { staticUrl: '/assets/' });
|
|
626
|
-
|
|
627
|
-
```
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
=== "ES Modules"
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
```javascript
|
|
636
|
-
|
|
637
|
-
import { compile } from 'miki-template';
|
|
638
|
-
|
|
639
|
-
const template = compile(source, { staticUrl: '/assets/' });
|
|
640
|
-
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
### url
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
Build a URL from a route name.
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
=== "Basic"
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
```html
|
|
658
|
-
|
|
659
|
-
<a href="{% url 'user.profile' user.id %}">Profile</a>
|
|
660
|
-
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
=== "With keyword arguments"
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
```html
|
|
670
|
-
|
|
671
|
-
{% url 'posts.show' post.id tab='comments' %}
|
|
672
|
-
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
=== "Deep routing with dots"
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
```html
|
|
682
|
-
|
|
683
|
-
{% url 'user.profile.posts.show' user.id post.id %}
|
|
684
|
-
|
|
685
|
-
```
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
Configure with a custom resolver:
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
=== "CommonJS"
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
```javascript
|
|
698
|
-
|
|
699
|
-
const { compile } = require('miki-template');
|
|
700
|
-
|
|
701
|
-
const template = compile(source, {
|
|
702
|
-
|
|
703
|
-
urlHelper: (routeName, ...args) => {
|
|
704
|
-
|
|
705
|
-
// Convert "user.profile" + [42] → "/user/profile/42"
|
|
706
|
-
|
|
707
|
-
return '/' + routeName.split('.').join('/') + '/' + args.join('/');
|
|
708
|
-
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
});
|
|
712
|
-
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
=== "ES Modules"
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
```javascript
|
|
722
|
-
|
|
723
|
-
import { compile } from 'miki-template';
|
|
724
|
-
|
|
725
|
-
const template = compile(source, {
|
|
726
|
-
|
|
727
|
-
urlHelper: (routeName, ...args) => {
|
|
728
|
-
|
|
729
|
-
return '/' + routeName.split('.').join('/') + '/' + args.join('/');
|
|
730
|
-
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
});
|
|
734
|
-
|
|
735
|
-
```
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
### regroup
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
Group a list by a common attribute.
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
```html
|
|
748
|
-
|
|
749
|
-
{% regroup people by gender as departments %}
|
|
750
|
-
|
|
751
|
-
{% for dept in departments %}
|
|
752
|
-
|
|
753
|
-
<h3>{{ dept.grouper }}</h3>
|
|
754
|
-
|
|
755
|
-
{% for person in dept.list %}
|
|
756
|
-
|
|
757
|
-
<p>{{ person.name }}</p>
|
|
758
|
-
|
|
759
|
-
{% endfor %}
|
|
760
|
-
|
|
761
|
-
{% endfor %}
|
|
762
|
-
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
You can also use `regroup` as a filter inside a `{% for %}` loop:
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
```html
|
|
772
|
-
|
|
773
|
-
{% for group in items|regroup:"category" %}
|
|
774
|
-
|
|
775
|
-
<h3>{{ group.grouper }}</h3>
|
|
776
|
-
|
|
777
|
-
{% for item in group.list %}
|
|
778
|
-
|
|
779
|
-
<p>{{ item.name }}</p>
|
|
780
|
-
|
|
781
|
-
{% endfor %}
|
|
782
|
-
|
|
783
|
-
{% endfor %}
|
|
784
|
-
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
### spaceless
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
Remove whitespace between HTML tags.
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
```html
|
|
798
|
-
|
|
799
|
-
{% spaceless %}
|
|
800
|
-
|
|
801
|
-
<div>
|
|
802
|
-
|
|
803
|
-
<span> hello </span>
|
|
804
|
-
|
|
805
|
-
</div>
|
|
806
|
-
|
|
807
|
-
{% endspaceless %}
|
|
808
|
-
|
|
809
|
-
```
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
Output: `<div><span> hello </span></div>`
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
### widthratio
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
Calculate ratios for progress bars or scaling.
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
```html
|
|
826
|
-
|
|
827
|
-
<!-- Calculate 25 out of 100 scaled to max-width 150 -->
|
|
828
|
-
|
|
829
|
-
{% widthratio score 100 150 %}
|
|
830
|
-
|
|
831
|
-
<!-- → 37 (floor of 25/100*150) -->
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
<!-- Progress bar width -->
|
|
836
|
-
|
|
837
|
-
<div class="bar" style="width: {% widthratio value max_value 100 %}px;"></div>
|
|
838
|
-
|
|
839
|
-
```
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
### debug
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
Dump the current template context for debugging.
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
```html
|
|
852
|
-
|
|
853
|
-
{% debug %}
|
|
854
|
-
|
|
855
|
-
```
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
Outputs a `<pre>` block with all context variables.
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
---
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
## Security Tags
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
### csrf_token
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
Output a hidden CSRF token input.
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
```html
|
|
880
|
-
|
|
881
|
-
<form method="post">
|
|
882
|
-
|
|
883
|
-
{% csrf_token %}
|
|
884
|
-
|
|
885
|
-
<button type="submit">Submit</button>
|
|
886
|
-
|
|
887
|
-
</form>
|
|
888
|
-
|
|
889
|
-
```
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
The token value is HTML-escaped to prevent attribute injection. Requires `csrf_token` to be present in the template context.
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
=== "CommonJS (Express middleware)"
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
```javascript
|
|
902
|
-
|
|
903
|
-
app.use((req, res, next) => {
|
|
904
|
-
|
|
905
|
-
res.locals.csrf_token = req.csrfToken();
|
|
906
|
-
|
|
907
|
-
next();
|
|
908
|
-
|
|
909
|
-
});
|
|
910
|
-
|
|
911
|
-
```
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
=== "ES Modules"
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
```javascript
|
|
920
|
-
|
|
921
|
-
app.use((req, res, next) => {
|
|
922
|
-
|
|
923
|
-
res.locals.csrf_token = req.csrfToken();
|
|
924
|
-
|
|
925
|
-
next();
|
|
926
|
-
|
|
927
|
-
});
|
|
928
|
-
|
|
929
|
-
```
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
### csp_nonce_attr
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
Output a `nonce` attribute when `csp_nonce` is in the context.
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
```html
|
|
942
|
-
|
|
943
|
-
<script {% csp_nonce_attr %} src="/js/app.js"></script>
|
|
944
|
-
|
|
945
|
-
```
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
If `csp_nonce` is present in context, the output is:
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
```html
|
|
954
|
-
|
|
955
|
-
<script nonce="abc123" src="/js/app.js"></script>
|
|
956
|
-
|
|
957
|
-
```
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
If `csp_nonce` is not present, the tag outputs nothing.
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
---
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
## Comments and Raw Output
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
### comment / endcomment
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
Block comments ignored during parsing.
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
```html
|
|
982
|
-
|
|
983
|
-
{% comment %}
|
|
984
|
-
|
|
985
|
-
This is a comment.
|
|
986
|
-
|
|
987
|
-
It can span multiple lines.
|
|
988
|
-
|
|
989
|
-
{% endcomment %}
|
|
990
|
-
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
### verbatim / endverbatim
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
Treat content as raw text — template syntax is not parsed.
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
```html
|
|
1004
|
-
|
|
1005
|
-
{% verbatim %}
|
|
1006
|
-
|
|
1007
|
-
This will NOT be parsed: {{ user.name }}
|
|
1008
|
-
|
|
1009
|
-
And this won't either: {% if x %}
|
|
1010
|
-
|
|
1011
|
-
{% endverbatim %}
|
|
1012
|
-
|
|
1013
|
-
```
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
You can also name a verbatim block:
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
```html
|
|
1022
|
-
|
|
1023
|
-
{% verbatim myscript %}
|
|
1024
|
-
|
|
1025
|
-
{{ angularExpression }}
|
|
1026
|
-
|
|
1027
|
-
{% endverbatim %}
|
|
1028
|
-
|
|
1029
|
-
```
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
---
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
## Autoescape
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
Control HTML escaping for a block.
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
```html
|
|
1046
|
-
|
|
1047
|
-
{% autoescape on %}
|
|
1048
|
-
|
|
1049
|
-
{{ user_input }} {# escaped → <script>... #}
|
|
1050
|
-
|
|
1051
|
-
{% endautoescape %}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
{% autoescape off %}
|
|
1056
|
-
|
|
1057
|
-
{{ trusted_html }} {# not escaped → raw HTML #}
|
|
1058
|
-
|
|
1059
|
-
{% endautoescape %}
|
|
1060
|
-
|
|
1061
|
-
```
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
---
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
## Library Loading
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
### load
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
Activate a template library. Built-in libraries (`humanize`, `cache`, `lorem`) are auto-activated — you only need `{% load %}` for custom libraries you've registered.
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
```html
|
|
1082
|
-
|
|
1083
|
-
{% load humanize %}
|
|
1084
|
-
|
|
1085
|
-
{{ views|intcomma }}
|
|
1086
|
-
|
|
1087
|
-
{{ count|ordinal }}
|
|
1088
|
-
|
|
1089
|
-
```
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
=== "CommonJS (registering a library)"
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
```javascript
|
|
1098
|
-
|
|
1099
|
-
const { registerLibrary } = require('miki-template');
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
registerLibrary('myutils', {
|
|
1104
|
-
|
|
1105
|
-
filters: {
|
|
1106
|
-
|
|
1107
|
-
shout: (val) => String(val).toUpperCase() + '!'
|
|
1108
|
-
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
});
|
|
1112
|
-
|
|
1113
|
-
```
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
=== "ES Modules"
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
```javascript
|
|
1122
|
-
|
|
1123
|
-
import { registerLibrary } from 'miki-template';
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
registerLibrary('myutils', {
|
|
1128
|
-
|
|
1129
|
-
filters: {
|
|
1130
|
-
|
|
1131
|
-
shout: (val) => String(val).toUpperCase() + '!'
|
|
1132
|
-
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
});
|
|
1136
|
-
|
|
1137
|
-
```
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
Then use in templates:
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
```html
|
|
1146
|
-
|
|
1147
|
-
{% load myutils %}
|
|
1148
|
-
|
|
1149
|
-
{{ name|shout }}
|
|
1150
|
-
|
|
1151
|
-
```
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
**Built-in libraries:**
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
- `i18n` — `{% trans %}`, `{% blocktrans %}`, `{% language %}`
|
|
1160
|
-
|
|
1161
|
-
- `humanize` — `intcomma`, `intword`, `apnumber`, `ordinal`, `naturalday`
|
|
1162
|
-
|
|
1163
|
-
- `cache` — `{% cache timeout key %}...{% endcache %}`
|
|
1164
|
-
|
|
1165
|
-
- `lorem` — `{% lorem %}` tag and `lorem` filter
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
---
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
## Template Tags
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
### templatetag
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
Output literal template tag tokens. Useful when generating documentation or when the template syntax conflicts with another templating layer.
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
```html
|
|
1186
|
-
|
|
1187
|
-
{% templatetag openblock %} if user.is_admin {% templatetag closeblock %}
|
|
1188
|
-
|
|
1189
|
-
<!-- Renders: {% if user.is_admin %} -->
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
{% templatetag openvariable %} name {% templatetag closevariable %}
|
|
1194
|
-
|
|
1195
|
-
<!-- Renders: {{ name }} -->
|
|
1196
|
-
|
|
1197
|
-
```
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
Available tokens:
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
| Token | Output |
|
|
1206
|
-
|
|
1207
|
-
|-------|--------|
|
|
1208
|
-
|
|
1209
|
-
| `openblock` | `{%` |
|
|
1210
|
-
|
|
1211
|
-
| `closeblock` | `%}` |
|
|
1212
|
-
|
|
1213
|
-
| `openvariable` | `{{` |
|
|
1214
|
-
|
|
1215
|
-
| `closevariable` | `}}` |
|
|
1216
|
-
|
|
1217
|
-
| `openbrace` | `{` |
|
|
1218
|
-
|
|
1219
|
-
| `closebrace` | `}` |
|
|
1220
|
-
|
|
1221
|
-
| `opencomment` | `{#` |
|
|
1222
|
-
|
|
1223
|
-
| `closecomment` | `#}` |
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
---
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
## Inheritance Tags
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
### extends
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
Inherit from a parent template.
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
```html
|
|
1244
|
-
|
|
1245
|
-
{% extends "base.html" %}
|
|
1246
|
-
|
|
1247
|
-
```
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
Can use expressions for dynamic parent selection:
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
```html
|
|
1256
|
-
|
|
1257
|
-
{% extends device|default:"desktop/base.html" %}
|
|
1258
|
-
|
|
1259
|
-
```
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
**Security:** Path traversal is blocked — `{% extends "../../etc/passwd" %}` is rejected.
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
### block / endblock
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
Define a block that can be overridden by child templates.
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
```html
|
|
1276
|
-
|
|
1277
|
-
<!-- base.html -->
|
|
1278
|
-
|
|
1279
|
-
<html>
|
|
1280
|
-
|
|
1281
|
-
<body>
|
|
1282
|
-
|
|
1283
|
-
{% block content %}Default content{% endblock %}
|
|
1284
|
-
|
|
1285
|
-
</body>
|
|
1286
|
-
|
|
1287
|
-
</html>
|
|
1288
|
-
|
|
1289
|
-
```
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
```html
|
|
1294
|
-
|
|
1295
|
-
<!-- child.html -->
|
|
1296
|
-
|
|
1297
|
-
{% extends "base.html" %}
|
|
1298
|
-
|
|
1299
|
-
{% block content %}
|
|
1300
|
-
|
|
1301
|
-
<h1>Child content</h1>
|
|
1302
|
-
|
|
1303
|
-
{{ block.super }}
|
|
1304
|
-
|
|
1305
|
-
{% endblock %}
|
|
1306
|
-
|
|
1307
|
-
```
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
### block.super
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
A special variable (not a tag). When used inside a `{% block %}`, it renders the parent template's version of that block.
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
See [Template Inheritance](template-inheritance.md) for a detailed guide.
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
### include
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
Include another template's content inline.
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
```html
|
|
1332
|
-
|
|
1333
|
-
{% include "header.html" %}
|
|
1334
|
-
|
|
1335
|
-
{% include "header.html" with title="Hello" %}
|
|
1336
|
-
|
|
1337
|
-
{% include "header.html#partial_name" %}
|
|
1338
|
-
|
|
1339
|
-
{% include "header.html" with title="Hello" %}
|
|
1340
|
-
|
|
1341
|
-
```
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
**Security:** Path traversal is blocked.
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
---
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
## Partial Tags
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
### partialdef / endpartialdef
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
Define a reusable partial block.
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
```html
|
|
1366
|
-
|
|
1367
|
-
{% partialdef card %}
|
|
1368
|
-
|
|
1369
|
-
<div class="card">
|
|
1370
|
-
|
|
1371
|
-
<h3>{{ title|default:"Untitled" }}</h3>
|
|
1372
|
-
|
|
1373
|
-
<p>{{ body|truncatewords:30 }}</p>
|
|
1374
|
-
|
|
1375
|
-
{% if featured %}<em>Featured</em>{% endif %}
|
|
1376
|
-
|
|
1377
|
-
</div>
|
|
1378
|
-
|
|
1379
|
-
{% endpartialdef %}
|
|
1380
|
-
|
|
1381
|
-
```
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
**Options:**
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
| Option | Description |
|
|
1390
|
-
|
|
1391
|
-
|--------|-------------|
|
|
1392
|
-
|
|
1393
|
-
| `inline` | Renders the definition inline at its location during parse (the body appears in output AND registers for later use). |
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
```html
|
|
1398
|
-
|
|
1399
|
-
{% partialdef greeting inline %}
|
|
1400
|
-
|
|
1401
|
-
Hello {{ name }}!
|
|
1402
|
-
|
|
1403
|
-
{% endpartialdef %}
|
|
1404
|
-
|
|
1405
|
-
<!-- Above line ALSO outputs "Hello World!" when rendered -->
|
|
1406
|
-
|
|
1407
|
-
```
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
**Programmatic access:**
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
=== "CommonJS"
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
```javascript
|
|
1420
|
-
|
|
1421
|
-
const { compile } = require('miki-template');
|
|
1422
|
-
|
|
1423
|
-
const compiled = compile(template);
|
|
1424
|
-
|
|
1425
|
-
compiled.renderPartial('card', { title: 'Hi', body: 'There' });
|
|
1426
|
-
|
|
1427
|
-
```
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
=== "ES Modules"
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
```javascript
|
|
1436
|
-
|
|
1437
|
-
import { compile } from 'miki-template';
|
|
1438
|
-
|
|
1439
|
-
const compiled = compile(template);
|
|
1440
|
-
|
|
1441
|
-
compiled.renderPartial('card', { title: 'Hi', body: 'There' });
|
|
1442
|
-
|
|
1443
|
-
```
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
### partial
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
Render a named partial.
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
```html
|
|
1456
|
-
|
|
1457
|
-
{% partial card %}
|
|
1458
|
-
|
|
1459
|
-
{% partial card with title="Custom" body="World" %}
|
|
1460
|
-
|
|
1461
|
-
{% partial greeting with name=user.name %}
|
|
1462
|
-
|
|
1463
|
-
```
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
See [Partial Templates](partial-templates.md) for a detailed guide.
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
---
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
## i18n Tags
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
### trans
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
Translate a string.
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
```html
|
|
1488
|
-
|
|
1489
|
-
{% trans "Hello, world!" %}
|
|
1490
|
-
|
|
1491
|
-
{% trans "Hello, %s!" name=user.name %}
|
|
1492
|
-
|
|
1493
|
-
{% trans context "verb" "He runs" %}
|
|
1494
|
-
|
|
1495
|
-
```
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
### blocktrans / endblocktrans
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
Translate a block of text with variable interpolation and pluralization.
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
```html
|
|
1508
|
-
|
|
1509
|
-
{% blocktrans with name=user.name %}
|
|
1510
|
-
|
|
1511
|
-
Hello, {{ name }}!
|
|
1512
|
-
|
|
1513
|
-
{% endblocktrans %}
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
{% blocktrans count items|length %}
|
|
1518
|
-
|
|
1519
|
-
{{ count }} item
|
|
1520
|
-
|
|
1521
|
-
{% plural %}
|
|
1522
|
-
|
|
1523
|
-
{{ count }} items
|
|
1524
|
-
|
|
1525
|
-
{% endblocktrans %}
|
|
1526
|
-
|
|
1527
|
-
```
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
### language / endlanguage
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
Switch language temporarily for a block.
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
```html
|
|
1540
|
-
|
|
1541
|
-
{% language "fr" %}
|
|
1542
|
-
|
|
1543
|
-
{% trans "Hello" %} → renders in French
|
|
1544
|
-
|
|
1545
|
-
{% endlanguage %}
|
|
1546
|
-
|
|
1547
|
-
```
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
---
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
## Next Steps
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
- [Filters](./filters.md)
|
|
1560
|
-
|
|
1561
|
-
- [Template Inheritance](./template-inheritance.md)
|
|
1562
|
-
|
|
1563
|
-
- [Partial Templates](./partial-templates.md)
|
|
1564
|
-
|
|
1565
|
-
- [Custom Tags](./custom-tags.md)
|
|
1566
|
-
|