1 | # Core tags and attributes
|
2 |
|
3 | Much 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 |
|
7 | Like 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 |
|
21 | They 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 |
|
39 | The `<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 |
|
49 | Like 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 |
|
51 | The 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 |
|
62 | The 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 |
|
76 | Like [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 |
|
78 | The 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 |
|
94 | The 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 |
|
107 | The 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 |
|
117 | You 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 |
|
159 | You 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 |
|
188 | Macros create reusable markup fragments for later use in the same template they were defined in.
|
189 |
|
190 | The `<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 |
|
208 | Macros 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 |
|
229 | Macros 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 |
|
253 | The `<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 |
|
284 | Optional 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 |
|
317 | If you do not want escaping, use [`<include-html>`](#include-html) instead.
|
318 |
|
319 | ## `<include-html>`
|
320 |
|
321 | Like `<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 |
|
329 | Marko 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 |
|
349 | The following tags and attributes are deprecated, but you might see them in older code.
|
350 |
|
351 | ### `marko-preserve-whitespace`
|
352 |
|
353 | Instead, preserve whitespace with the `preserve-whitespace` attribute:
|
354 |
|
355 | ```marko
|
356 | style {
|
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 |
|
370 | The `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 <b malformed-attribute="> Hello ${THIS IS NOT VALID}!
|
391 | </b> big text block
|
392 | </p>
|
393 | ```
|