UNPKG

11.3 kBMarkdownView Raw
1# Core tags and attributes
2
3Much like [HTML has its own native tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element), Marko includes **core tags** and **global attributes** for declaratively building modern applications.
4
5## `<if>`, `<else-if>`, `<else>`
6
7Like the [equivalent JavaScript statements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else), these tags render [conditional content](./conditionals-and-lists.md#conditionals):
8
9```marko
10<if(arriving)>
11 Hey there
12</if>
13<else-if(leaving)>
14 Bye now
15</else-if>
16<else>
17 What’s up?
18</else>
19```
20
21They support any JavaScript expression in their [tag arguments](./syntax.md#arguments):
22
23```marko
24<if(Math.random() > 0.5)>
25 <p>50% chance to see this</p>
26</if>
27```
28
29> **Note:** The [alternate conditional attribute syntax is deprecated](https://github.com/marko-js/marko/wiki/Deprecation:-control-flow-attributes):
30>
31> ```marko
32> <p if(arriving)>Hey there</p>
33> <p else-if(leaving)>Bye now</p>
34> <p else>What’s up?</p>
35> ```
36
37## `<for>`
38
39The `<for>` tag iterates over [arrays/array-likes](#iterating-over-a-list), [object properties](#iterating-over-an-objects-properties), and [ranges of numbers](#iterating-between-a-range-of-numbers).
40
41> **Note:** You may see `for()` as a tag or attribute. This [kinda-like-JS-but-not-really](https://github.com/marko-js/marko/issues/577) syntax [is deprecated](https://github.com/marko-js/marko/pull/1238):
42>
43> ```marko
44> <li for(color in colors)>${color}</li>
45> ```
46
47### Iterating over a list
48
49Like the [JavaScript `for...of` loop statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of), giving `<for>`’s `of` attribute a value will loop over that value as an array or iterable.
50
51The current **item**, **index**, and the **iterating list** are provided as [tag parameters](./syntax.md#parameters):
52
53```marko
54$ const colors = ["red", "green", "blue"];
55<ol>
56 <for|color, index, colorList| of=colors>
57 <li value=index>${color}</li>
58 </for>
59</ol>
60```
61
62The output HTML would be:
63
64```html
65<ol>
66 <li value="0">red</li>
67 <li value="1">green</li>
68 <li value="2">blue</li>
69</ol>
70```
71
72> **Pro Tip**: `<for>`’s `of` attribute can loop over any iterable, just like JavaScript’s [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of). This includes strings, `NodeList`s, `Set`s… any object with zero-indexed numeric properties and a `.length`, basically.
73
74### Iterating over an object’s properties
75
76Like [JavaScript’s `for...in` loop statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in), giving `<for>` an object as its `in` attribute will loop over that object’s properties.
77
78The current **property name** and **property value** are provided as [tag parameters](./syntax.md#parameters):
79
80```marko
81$ const settings = {
82 "Dark Mode": false,
83 "Fullscreen": true
84};
85
86<dl>
87 <for|name, enabled| in=settings>
88 <dt>${name}:</dt>
89 <dd>${enabled ? "on" : "off"}</dd>
90 </for>
91</dl>
92```
93
94The output HTML would be:
95
96```html
97<dl>
98 <dt>Dark Mode:</dt>
99 <dd>off</dd>
100 <dt>Fullscreen:</dt>
101 <dd>on</dd>
102</dl>
103```
104
105### Iterating between a range of numbers
106
107The final `<for>` variant loops between two numbers, by providing `from` and `to` attributes. The current number in the range will be provided as a [tag parameter](./syntax.md#parameters):
108
109```marko
110<ol type="I">
111 <for|i| from=0 to=10>
112 <li value=i>${i}</li>
113 </for>
114</ol>
115```
116
117You can also pass an optional `step` attribute, which defaults to 1 otherwise. `step` lets you increment by a specific amount:
118
119```marko
120<ol type="I">
121 <for|i| from=0 to=10 step=2>
122 <li value=i>${i}</li>
123 </for>
124</ol>
125```
126
127…becomes:
128
129```marko
130<ol type="I">
131 <li value="0">0</li>
132 <li value="2">2</li>
133 <li value="4">4</li>
134 <li value="6">6</li>
135 <li value="8">8</li>
136 <li value="10">10</li>
137</ol>
138```
139
140> **ProTip:** This syntax is for generating numbers from nothing. Don’t use it to iterate over an object, like so:
141>
142> ```marko
143> <!-- Inefficient code, do not copy -->
144> <ul>
145> <for|i| from=0 to=(myArray.length - 1)>
146> <li>${myArray[i]}</li>
147> </for>
148> </ul>
149> ```
150>
151> Use [`<for of>`](#iterating-over-a-list) instead.
152
153## `<while>`
154
155> **Warning:** Using `<while>` is not recommended. Instead, replicate it with [an iterable and `<for>`](#iterating-over-a-list).
156>
157> In the future, Marko may restrict value mutation during rendering, for runtime optimizations.
158
159You can repeat a chunk of markup _until a condition is met_ with the `while` tag:
160
161```marko
162$ let n = 0;
163
164<while(n < 4)>
165 <p>${n++}</p>
166</while>
167```
168
169…becomes:
170
171```html
172<p>0</p>
173<p>1</p>
174<p>2</p>
175<p>3</p>
176```
177
178> **Note:** [`while` as an attribute is deprecated](https://github.com/marko-js/marko/wiki/Deprecation:-control-flow-attributes):
179>
180> ```marko
181> $ let n = 0;
182>
183> <p while(n < 4)>${n++}</p>
184> ```
185
186## `<macro>`
187
188Macros create reusable markup fragments for later use in the same template they were defined in.
189
190The `<macro>` tag defines a macro as a tag via the `name` attribute. For example, the following macro is registered as the `<greeting>` tag:
191
192```marko
193<macro name="greeting">
194 <p>Welcome!</p>
195</macro>
196
197<greeting/>
198<greeting/>
199```
200
201…the output HTML would be:
202
203```html
204<p>Welcome!</p>
205<p>Welcome!</p>
206```
207
208Macros become more useful with [tag parameters](./syntax.md#parameters), allowing complex templates. In this next example, `<greeting>` can now receive `firstName` and `count` parameters from its parent:
209
210```marko
211<macro|{ firstName, count }| name="greeting">
212 <p>Hello ${firstName}!
213 <output>You have ${count} new messages.</output>
214 </p>
215</macro>
216
217<greeting firstName="Frank" count=20/>
218```
219
220…the output HTML would be:
221
222```html
223<p>
224 Hello Frank!
225 <output>You have 20 new messages.</output>
226</p>
227```
228
229Macros receive input like components do, including [a `renderBody` for provided body content](./body-content.md):
230
231```marko
232<macro|{ renderBody }| name="special-heading">
233 <h1>
234 <${renderBody}/>!
235 </h1>
236</macro>
237
238<special-heading>
239 Hello
240</special-heading>
241```
242
243…the output HTML would be:
244
245```html
246<h1>Hello!</h1>
247```
248
249> **ProTip:** You can use a macro inside itself for recursive layouts, like displaying directory contents.
250
251## `<await>`
252
253The `<await>` tag **renders markup asynchronously using a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)**.
254
255- Its `<@then>` [attribute tag](./syntax.md#attribute-tag) displays when the Promise _resolves_, optionally receiving the resolved value as a [tag parameter](./syntax.md#parameters).
256- Its `<@catch>` attribute tag displays when the Promise _rejects_, optionally receiving the rejected value as a tag parameter.
257- Its optional `<@placeholder>` attribute tag displays while the Promise is pending.
258
259```marko
260$ const personRequest = new Promise((resolve, reject) => {
261 setTimeout(() => resolve({ name: 'Frank' }), 1000);
262});
263
264<await(personPromise)>
265 <@placeholder>
266 <!-- Displays while promise is pending -->
267 <label>Loading…
268 <progress></progress>
269 </label>
270 </@placeholder>
271
272 <@then|person|>
273 <!-- Displays if promise resolves -->
274 <p>Hello ${person.name}!</p>
275 </@then>
276
277 <@catch|err|>
278 <!-- Displays if promise rejects -->
279 ${err.name} error: ${err.message}
280 </@catch>
281</await>
282```
283
284Optional attributes for `<await>`:
285
286| Attribute | Type | Description |
287| ---------------: | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
288| `timeout` | integer | An optional timeout. If reached, rejects the promise with a `TimeoutError`. |
289| `name` | string | Improves debugging and ensures ordering with the `show-after` attribute. |
290| `show-after` | string | Another `<await>` tag’s `name`. With `client-reorder`, ensures that the current `<await>` block will always show after the named `<await>`. |
291| `client-reorder` | boolean | If true, anything after this `<await>` will be server-rendered before the Promise completes, then the fulfilled Promise’s result will be updated with client-side JavaScript. |
292
293> **Pro Tip**: When using `timeout`, you can distinguish between `TimeoutError`s and promise rejections by checking the error’s `name`:
294>
295> ```marko
296> <await(slowPromise) timeout=5000>
297> <@then>Done</@then>
298> <@catch|err|>
299> <if(err.name === "TimeoutError")>
300> Took too long to fetch the data!
301> </if>
302> <else>
303> Promise failed with ${err.message}.
304> </else>
305> </@catch>
306> </await>
307> ```
308
309## `<include-text>`
310
311`<include-text>` inlines text files into a template, escaping HTML syntax characters (`<`, `"`, etc.).
312
313```marko
314<include-text('./foo.txt')/>
315```
316
317If you do not want escaping, use [`<include-html>`](#include-html) instead.
318
319## `<include-html>`
320
321Like `<include-text>`, `<include-html>` inlines the contents of a file. However, this tag **does _not_ escape** special HTML characters.
322
323```marko
324<include-html('./foo.html')/>
325```
326
327## `<html-comment>`
328
329Marko removes HTML comment tags from its output. But if you need comments in the output, that’s what `<html-comment>` is for:
330
331```marko
332<html-comment>[if IE]><script src="html-shiv.js"></script><![endif]</html-comment>
333```
334
335…becomes:
336
337```html
338<!--[if IE]><script src="html-shiv.js"></script><![endif]-->
339```
340
341> **Note:** You might see the **deprecated** `<marko-compiler-options>` tag used to configure comments for the template:
342>
343> ```marko
344> <marko-compiler-options preserve-comments/>
345> ```
346
347## Deprecated
348
349The following tags and attributes are deprecated, but you might see them in older code.
350
351### `marko-preserve-whitespace`
352
353Instead, preserve whitespace with the `preserve-whitespace` attribute:
354
355```marko
356style {
357 .lang-python {
358 white-space: pre-wrap;
359 }
360}
361
362<p>You’ll get an error with that line of Python,
363 as it has one too many spaces as indentation:
364 <code.lang-python marko-preserve-whitespace> <mark> </mark>frobulate()</code>
365</p>
366```
367
368### `marko-body`
369
370The `marko-body` attribute controls how a tag’s body content is parsed, with the following possible values:
371
372- `html` (default) — Body content is parsed as HTML.
373- `static-text` — Body content is parsed as static text, ignoring HTML tags and [dynamic text `${placeholders}`](./syntax.md/#dynamic-text).
374- `parsed-text` — Body content is parsed as text, ignoring HTML tags. Does not ignore `${placeholders}`.
375
376```marko
377<p marko-body="static-text">
378 This is just one
379 <b malformed-attribute=">
380 Hello ${THIS IS NOT VALID}!
381 </b>
382 big text block
383</p>
384```
385
386…becomes:
387
388```html
389<p>
390 This is just one &lt;b malformed-attribute="&gt; Hello ${THIS IS NOT VALID}!
391 &lt;/b&gt; big text block
392</p>
393```