davydog187
Improve the ability to conditionally render streams (and lists)
I’ve been enjoying the new Streams API, but I keep needing to work around the lack of access to the underlying items. One issue that consistently comes up is the ability to query if a stream is in an empty state.
Example
Let’s use the LiveBeats application as an example. Currently, when no songs are uploaded, the table of songs renders with no rows. To improve the design, we could implement a call to action that replaces the table, instructing the user on how to upload their first song. This is a common pattern in UI development, where an empty list may have a different treatment than a list of size N > 0.
Limitations of the current design
Ability to query a Stream’s empty state
As of this writing, there is not an API that allows for a user to query if a stream is empty or not. While a user can add additional assigns that count or track of a stream is empty, it poses bookkeeping challenges in a complex application. More importantly, this is trivial to implement with a traditional list, where streams make the ceremony of tracking the empty state much more complex. This is particularly confusing for new users of Elixir and LiveView
DSL for conditionally rendering an empty list
Frameworks/languages such as svelte, surface, and Python implement a for/else idiom. This effectively allows the user to conditionally handle/render an empty list without adding multiple conditionals. This is somewhat of a luxury, but I find it to be a powerful idiom.
Proposal
Considering the challenges outlined above, I propose the following enhancements to the Phoenix Live View library:
1. Addition of a stream_empty?/1 function to the Stream API
I propose introducing a stream_empty?/1 function to the Stream API. This function would return a boolean value representing whether or not the stream is in an empty state. With this new function, users can conveniently query the state of a stream without the need for additional assigns or complex bookkeeping.
API Usage:
<div :if={stream_empty?(@streams.songs)}>
Click here to upload your first song!
</div>
2. Implementation of a for/else idiom
I suggest extending Phoenix Live View’s template syntax to support a for/else idiom. This would allow developers to conditionally handle and render empty lists and streams directly within their templates, without needing to manage multiple conditionals.
Proposed Syntax:
<%= for item <- @stream.songs do %>
<!-- Render table -->
<% else %>
<!-- Render call to action -->
<% end %>
These changes would simplify the process of handling empty states. They would also make the library more accessible for new users by reducing the complexity of common tasks, and enhance its functionality for experienced developers.
Most Liked
chrismccord
Tracking stream details has been on my mind from the beginning (stream size, key space, etc), but I’ve been hesitant to do it so far because it requires more state bookkeeping (and thus memory) when folks don’t always need it. That said, we could make it opt-in, but it’s not something I’ve been able to prioritize yet.
The LiveView docs (which I just pushed ahead of 0.19), have an example for infinite scrolling that tracks the “you’ve reached the beginning of time” state in an @end_of_timeline? assign, which is set as the stream is paginated based on whether a next page triggers zero results. So in cases like this, a simple assign that you set programmatically as you stream is all you need.
I can definitely see how something built-in would be useful if all you want to know is if you’ve streamed anything or how many have been streamed. For now I encourage folks to track what state they need on the side. For conditional rendering, a simple assign with an if check is usually all you need, like in the linked example:
<ul
id="posts"
phx-update="stream"
phx-viewport-top={@page > 1 && "prev-page"}
phx-viewport-bottom={!@end_of_timeline? && "next-page"}
phx-page-loading
class={[
if(@end_of_timeline?, do: "pb-10", else: "pb-[calc(200vh)]"),
if(@page == 1, do: "pt-10", else: "pt-[calc(200vh)]")
]}
>
<li :for={{id, post} <- @streams.posts} id={id}>
<.post_card post={post}>
</li>
</ul>
<div :if={@end_of_timeline?} class="mt-5 text-[50px] text-center">
🎉 You made it to the beginning of time 🎉
</div>
```
codeanpeace
Haven’t tried this yet myself, but can’t you check if a streams empty like this?
<div :if={@streams.songs == []}>
Click here to upload your first song!
</div>
Update:
So that doesn’t work, turns out @streams.songs is a %Phoenix.LiveView.LiveStream{} struct.
That said, the %Phoenix.LiveView.LiveStream{} struct partially implements the Enumerable protocol which means you can query the state of the stream with something like this.
<div :if={Enum.count(@streams.songs) == 0}>
Click here to upload your first song!
</div>
sreyansjain
try Enum.count(@stream.songs) == 0
Its working for me







