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.
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.