Smarter attributes coming to Latte

David Grudl
Nette Core | 8282
+
0
-

I want to introduce new features for attribute rendering in Latte that will make working with templates easier and more type-safe. The main goal is to solve common issues with attribute rendering while preserving backward compatibility for existing code.

Boolean in attribute

<div foo="{$val}">

If $val is a string or number, it's clear what gets rendered. But what happens when $val contains a boolean?

<div foo="{=true}" bar="{=false}">
// renders as: <div foo="1" bar="">

This isn't ideal, because this behavior is rarely useful in practice

I therefore plan to release a version that will throw warnings if an attribute value is boolean (or array, see below), indicating that this type is not supported. The warning will directly reference the template name and line number.

Exception: boolean attributes

Boolean attributes (like checked or hidden) will accept boolean values. The value determines whether the attribute gets rendered or not (based on whether it's truthy):

<input hidden="{=true}" readonly="{=false}">
// renders as: <input hidden>

This behavioral change isn't a BC break, because currently it doesn't make sense for anyone to combine a boolean attribute with any value in a template.

Aria attributes

Another exception would be aria-attributes, where I want boolean attributes to be output as true and false, because they work with these values:

<button aria-expanded="{=true}" aria-checked="{=false}">
// renders as: <button aria-expanded="true" aria-checked="false">

Again, I believe this isn't a BC break, because currently it doesn't make sense for anyone to combine an aria attribute with a bool value in a template.

Data attributes

It might be useful if data-attributes behaved similarly to aria-attributes, i.e., that bool values would be accepted and output as true and false. But that would already represent a BC break. Therefore data-attributes don't represent any exception and will throw warnings.

I can imagine that the current behavior, where boolean gets typecast to "1" or "", works for you with data-attributes, that you rely on it. Then it's easiest to work around this by typecasting the value to string <div data-foo="{(string) $val}">. I understand that this is a case where this change would require some additional work.

If you'd instead want to go the route of true and false values, the easiest approach is probably to create a filter for it. I'm considering adding something like this to the standard toolkit.

Array in attribute

What happens when we try to pass an array into an attribute?

<div foo="{[]}">
// renders as: <div foo="Array">

In the new version, besides Latte throwing warnings when there's an array in an attribute value, I want such attributes not to render at all, because foo="Array" is never useful.

However, arrays will be allowed for style attributes:

<div style={[ backgroundColor: 'lightblue', fontSize: '16px' ]}>
// renders: <div style="background-color: lightblue; font-size: 16px">

Also for attributes that expect a space-separated list, like class (which actually makes it equivalent to n:class):

<div class={[ first: true, last: false, always ]}>
// renders: <div class="first always">

And finally for all data-* attributes, where it will serialize as JSON:

<div data-config={[ theme: 'dark', version: 2 ]}>
// renders: <div data-config='{"theme":"dark","version":2}'>

n:attr

The goal is also to completely unify how attributes are rendered using the n:attr tag with how they're rendered when we write them directly in template code.

Optional attributes

I'd like to eventually achieve more flexible conditional attribute rendering in the future. It would be possible to easily write data-attributes where the value would determine their presence or absence, so they would actually function like boolean attributes.

However, this is more complex from a compatibility standpoint, so I'll create a separate thread for that.