|
| 1 | +Smart HTML Attributes |
| 2 | +********************* |
| 3 | + |
| 4 | +.[perex] |
| 5 | +Latte 3.1 comes with a set of improvements that focuses on one of the most common activities in templates – printing HTML attributes. It brings more convenience, flexibility and security. |
| 6 | + |
| 7 | + |
| 8 | +Boolean Attributes |
| 9 | +================== |
| 10 | + |
| 11 | +HTML has a specific category of attributes where the value doesn't matter, but their presence does. These are attributes like `checked`, `disabled`, `selected`, `hidden`, `readonly`, `required`, etc. |
| 12 | + |
| 13 | +Previously, you had to use `n:attr` or conditions. In Latte 3.1, you can pass `true` or `false` directly to the attribute: |
| 14 | + |
| 15 | +```latte |
| 16 | +<input hidden={true} readonly={false}> |
| 17 | +``` |
| 18 | + |
| 19 | +Outputs: |
| 20 | + |
| 21 | +```latte |
| 22 | +<input hidden> |
| 23 | +``` |
| 24 | + |
| 25 | +If the value is `true`, the attribute is rendered. If `false`, it is omitted. This is intuitive and the code is much more readable. |
| 26 | + |
| 27 | +If you are using a JavaScript library that requires the presence/absence of an attribute and you want to achieve this behavior for other attributes as well (e.g. `data-` attributes), use the [toggle |filters#toggle] filter. |
| 28 | + |
| 29 | + |
| 30 | +Null Values |
| 31 | +=========== |
| 32 | + |
| 33 | +This is one of the most pleasant changes. Previously, if a variable was `null`, it printed as an empty string `""`. This often led to empty attributes in HTML like `class=""` or `title=""`. |
| 34 | + |
| 35 | +In Latte 3.1, a new universal rule applies: **A value of `null` means the attribute does not exist.** |
| 36 | + |
| 37 | +```latte |
| 38 | +<div title="{$title}"></div> |
| 39 | +``` |
| 40 | + |
| 41 | +If `$title` is `null`, the output is `<div></div>`. If it contains a string, e.g. "Hello", the output is `<div title="Hello"></div>`. Thanks to this, you don't have to wrap attributes in conditions. |
| 42 | + |
| 43 | +If you use filters, keep in mind that they usually convert `null` to a string (e.g. empty string). To prevent this, use the [nullsafe operator |syntax#Nullsafe Filters] `?|`: |
| 44 | + |
| 45 | +```latte |
| 46 | +<div title="{$title?|upper}"></div> |
| 47 | +``` |
| 48 | + |
| 49 | + |
| 50 | +Classes and Styles |
| 51 | +================== |
| 52 | + |
| 53 | +The `n:class` attribute is loved for its ability to compose classes from arrays and conditions. Now the standard `class` attribute gains this superpower as well. |
| 54 | + |
| 55 | +You can pass an array (even an associative one with conditions) to the `class` attribute, exactly as you are used to with `n:class`: |
| 56 | + |
| 57 | +```latte |
| 58 | +<div class="header"> |
| 59 | + <button class={[ |
| 60 | + 'btn', |
| 61 | + 'btn-primary', |
| 62 | + 'active' => $isActive, |
| 63 | + 'disabled' => $isDisabled |
| 64 | + ]}>Press me</button> |
| 65 | +</div> |
| 66 | +``` |
| 67 | + |
| 68 | +If `$isActive` is true and `$isDisabled` is false, it renders: |
| 69 | + |
| 70 | +```latte |
| 71 | +<div class="header"> |
| 72 | + <button class="btn btn-primary active">Press me</button> |
| 73 | +</div> |
| 74 | +``` |
| 75 | + |
| 76 | +The `style` attribute also gains similar flexibility. Instead of concatenating strings, you can pass an array: |
| 77 | + |
| 78 | +```latte |
| 79 | +<div style={[ |
| 80 | + background: 'lightblue', |
| 81 | + display: $isVisible ? 'block' : 'none' |
| 82 | +]}></div> |
| 83 | +``` |
| 84 | + |
| 85 | + |
| 86 | +Data Attributes |
| 87 | +=============== |
| 88 | + |
| 89 | +Often we need to pass configuration for JavaScript into HTML. Previously this was done via `json_encode`. Now you can simply pass an array or object to a `data-` attribute and Latte will serialize it to JSON: |
| 90 | + |
| 91 | +```latte |
| 92 | +<div data-config={[ theme: 'dark', version: 2 ]}></div> |
| 93 | +``` |
| 94 | + |
| 95 | +Outputs: |
| 96 | + |
| 97 | +```latte |
| 98 | +<div data-config='{"theme":"dark","version":2}'></div> |
| 99 | +``` |
| 100 | + |
| 101 | +Also, `true` and `false` are rendered as strings `"true"` and `"false"` (i.e. valid JSON). |
| 102 | + |
| 103 | + |
| 104 | +Aria Attributes |
| 105 | +=============== |
| 106 | + |
| 107 | +The WAI-ARIA specification requires text values `"true"` and `"false"` for boolean values. Latte handles this automatically for `aria-` attributes: |
| 108 | + |
| 109 | +```latte |
| 110 | +<button aria-expanded={true} aria-checked={false}></button> |
| 111 | +``` |
| 112 | + |
| 113 | +Outputs: |
| 114 | + |
| 115 | +```latte |
| 116 | +<button aria-expanded="true" aria-checked="false"></button> |
| 117 | +``` |
| 118 | + |
| 119 | + |
| 120 | +Type Checking |
| 121 | +============= |
| 122 | + |
| 123 | +Latte 3.1 checks attribute types to prevent you from printing nonsense. |
| 124 | + |
| 125 | +1. **Standard attributes (href, src, id...):** Expect a string or `null`. If they receive an array, object or boolean, Latte throws a warning and the attribute is omitted. |
| 126 | +2. **Boolean attributes (checked...):** Expect a boolean. |
| 127 | +3. **Special attributes (class, style, data-, aria-):** Have their own rules described above. |
| 128 | + |
| 129 | +This check helps you discover bugs in your code early. |
| 130 | + |
| 131 | + |
| 132 | +Migration from Latte 3.0 |
| 133 | +======================== |
| 134 | + |
| 135 | +Since the behavior of `null` changes (it used to print `""`, now it doesn't print anything) and `data-` attributes (booleans used to print `1`/`""`, now `"true"`/`"false"`), Latte offers a tool to help with migration. |
| 136 | + |
| 137 | +You can enable **migration warnings** (see [Develop |develop#Migration Warnings]), which will warn you during rendering if the output differs from Latte 3.0. |
| 138 | + |
| 139 | +If the new behavior is correct (e.g. you want the empty attribute to disappear), confirm it using the `|accept` filter to suppress the warning: |
| 140 | + |
| 141 | +```latte |
| 142 | +<div class="{$var|accept}"></div> |
| 143 | +``` |
| 144 | + |
| 145 | +If you want to keep the attribute as empty (e.g. `title=""`) instead of dropping it, use the null coalescing operator: |
| 146 | + |
| 147 | +```latte |
| 148 | +<div title={$var ?? ''}></div> |
| 149 | +``` |
| 150 | + |
| 151 | +Or, if you strictly require the old behavior (e.g. `"1"` for `true`), explicitly cast the value to string: |
| 152 | + |
| 153 | +```latte |
| 154 | +<div data-foo={(string) $bool}></div> |
| 155 | +``` |
| 156 | + |
| 157 | +Once all warnings are resolved, disable migration warnings and **remove all** `|accept` filters from your templates, as they are no longer needed. |
0 commit comments