Conditionally render element attribute in lv 0.17.9

Migrating templates from 0.15.x to 0.17.9.

Any ideas on how to conditionally render an element attribute?
Before I could simply do it as shown below, but it doesn’t work anymore:

<div <%= if @foo do %> my-attribute<% end %>>
</div>
1 Like

Try:

<div my-attribute={@foo}>
</div>

Falsy values (nil, false) will not show my-attribute on the element, true will render my-attribute

2 Likes

Thanks, but are you sure it won’t be rendered if falsy? Asking because I have plenty of templates to migrate and a non trivial amount of JS depending on such attributes.

Also, is this documented anywhere (haven’t found it in the guides)? If not, it should be.

are you sure it won’t be rendered if falsy?

Positive. The new HEEx syntax uses {} to interpolate values over <%= %>

<div data-my-attr={false}></div>

Will render like:

<div></div>

You can also do string interpolation for things like conditionally adding classes:
<div class={"px-4 #{if some_condition, do: "py-4"}"}></div>

If some_condition is true, you’ll get:

<div class="px-4 py-4"></div>

If false/nil, you’ll get:
<div class="px-4 "></div>

6 Likes

Thanks a lot!

You can also do something like this:

<div class={["px-4", some_condition && "py-4" ]}>
7 Likes

Did not know this - Much cleaner!

2 Likes

This is VERY interesting. Can you please point out where this is documented. I wonder if there’s more to it.

To rephrase: does the interpolation behave exactly like string interpolation but with change tracking?

Playing with examples now. Just noticed that the rendered output varies based on the attribute it is being defined for. For example:

<div class={ [ "foo", "bar"]}></div>

renders into

<div class="foo bar"></div>

(with a space character added between the two class names)

While:

<div something-else={ [ "foo", "bar"]}></div>

renders into:

<div something-else= "foobar"></div>

Again, will really appreciate if there are in-depth docs on this. Thanks.

1 Like

Only class has that behavior:

The following attribute values have special meaning:

true - if a value is true, the attribute is rendered with no value at all. For example, <input required={true}> is the same as <input required>;
false or nil - if a value is false or nil, the attribute is not rendered;
list (only for the class attribute) - each element of the list is processed as a different class. nil and false elements are discarded.

Read more here:

https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.Helpers.html#sigil_H/2-heex-extension-defining-attributes

2 Likes

Thanks!

Don’t understand why they chose to document this under sigil_H function as it applies to templates regardless of how their rendering is invoked (i.e. may as well be implicit), but it’s good to know where it is and that this is all to it.

1 Like

The docs link is no longer valid. I found equivalent on:

https://hexdocs.pm/phoenix_live_view/0.20.17/Phoenix.Component.html#sigil_H/2

But then the docs say:

  • false or nil - if a value is false or nil, the attribute is omitted. Some attributes may be rendered with an empty value, for optimization purposes, if it has the same effect as omitting. For example, <checkbox checked={false}> renders to <checkbox> while, <div class={false}> renders to <div class="">;

while in fact it seems to render <div class> instead.

What I am looking for though is a way do completely omit the class attribute, despite the “optimization purposes” (whatever they are) :wink: Any clues?

If the class attribute is rendered even if set to false or nil as such, I guess it’s an oversight.

I double-checked and it renders class="" indeed. It was browser’s devtools “elements” that displayed it as class alone. Thus at least this part behaves as mentioned in the docs but what if I don’t want the “optimization” and just want to get

<div id="advanced">

from

<div id="advanced" class={unless @advanced_show, do: "hidden"}>

It doesn’t have to be exactly as the second line above but you get the idea. Any simple way to achieve this?

I wouldn’t expect there to be an opt out of the optimization or it would be mentioned there. However I’d argue that whatever reason you have to need the class to be omitted instead of empty might be better off pushed to not be communicated via the existance of the class attribute.

Yes, you are right. And – TBH – I wouldn’t allow myself to depend on something “brittle” like the presence of the class attribute for an important piece of logic. It’s more of “can I easily get what I want?”. If I can’t that’s not the end of the world either. At least it doesn’t render class alone as I initially thought it did.