You probably already know that <span>{nil}</span> in a HEEX template produces <span> </span> when rendered. I find this very inconvenient because I can’t conditionally display a placeholder content via the :empty CSS pseudo-class, as it doesn’t tolerate whitespace at all. I know, there’s :blank but it’s still experimental and it’s not supported by any browser.
I encountered an article which tries to demonstrate how to check if a slot is empty. The solution falls apart as soon as you call slot_empty? with a slot from a list of slots. The following fragment makes the function report that both slots are not empty while in fact they are.
<.some_component>
<:item></:item>
<:item></:item>
</.some_component>
The following macro is inspired by Phoenix.Component.render_slot/2 and actually renders the slot and checks if it’s all whitespace. If it is, then it returns nil, otherwise - the rendered slot, ready to be put in the template.
defmacro maybe_render_slot(slot, argument \\ nil) do
# This is what `Phoenix.Component.render_slot/2` does with the twist
# that our function can be used outside of a HEEX template.
changed =
if Macro.Env.has_var?(__CALLER__, {:changed, Phoenix.LiveView.Engine}) do
Macro.var(:changed, Phoenix.LiveView.Engine)
end
quote do
rendered =
unquote(changed)
|> Phoenix.Component.__render_slot__(unquote(slot), unquote(argument))
|> Phoenix.HTML.Safe.to_iodata()
blank? =
rendered
# Using Erlang's API because it accepts IO lists and we can avoid
# "materialising" the IO list to a binary. Of course, one can compile
# the regular expression but I skipped it for brevity sake.
|> :re.run("^[[:space:]]*$")
|> then(&match?({:match, _}, &1))
if not blank? do
Phoenix.HTML.raw(rendered)
end
end
end
Then, in a template, you can use the macro to conditionally render a non-blank slot and use a sibling element as a placeholder content.
<p :for={item <- @items}>
<span :if={rendered = maybe_render_slot(item)}>{rendered}</span>
<span class="hidden only:inline">{@placeholder}</span>
</p>
I hope someone finds this helpful. I can’t wait for the browsers to support the :blank CSS pseudo-class so we don’t need to do such template gymnastics.






















