Currently, in September 2023, LiveView does not offer an easy way to check if the @streams
does not contain elements. The information is essential if we want a different UI for our empty state. After some research, I saw a post from “codeanpeace” on the Elixir Forum proposing using some Tailwind helpers. I decided to give it a try, and it works pretty great.
The final solution will be like this:
Now, let’s dive into the code. Our list is very straightforward. We added a boards
key with the list of “boards” to our stream and will use this later in the html.
use ReflectiveLoopWeb, :live_view
alias ReflectiveLoop.Retro
@impl true
end
# ...
end
I use a custom component to display a list that applies my desired style. I’ve omitted the non-essential parts. The critical factor is the render_slot(@new_item_action)
. Notice that it’s inside the div
that will have the items from the @streams
. The stacked_list component doesn’t know about the style of the slot, so we have the freedom to have lists with or without a custom empty state.
end
Now, for the html part, we will use some pseudo-classes to change the style of the new item button, considering if the list is empty or not. I opted to use a single link with some extra css applied when it is the only element in the list.
Let’s check how the component works. Again, I’ve omitted the non-essential parts. As you can see, it’s straightforward. What makes the “magic” is the only
pseudo-class that I’ve applied in the link.
<.stacked_list
id=
lines=
>
<:title :let= label=>
<%= board.name %>
</:title>
<:new_item_action>
<.link
id=
patch=
class=
>
<.icon name= class= />
<span class=>
Create a new board
</span>
</.link>
</:new_item_action>
</.stacked_list>
The structure needs to have a specific format. We have a div with two children, and we use Tailwind to display extra css when the link is the only child.
But there is an a
and a div
, so we always have two children, no? Actually, no. The tag is not rendered when no items are in the @streams
. In the empty state, the HTML will be a single child tag.
I have never considered using CSS to handle states, but this works well with streams. If you don’t use Tailwind, style your div with :only-child
, and you have the same result.