davydog187

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

chrismccord

Creator of Phoenix

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

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.

https://github.com/phoenixframework/phoenix_live_view/blob/8585ba7a3bddf8fdcb267071fdff0c27a155d75a/lib/phoenix_live_view/live_stream.ex#L61-L62

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

sreyansjain

try Enum.count(@stream.songs) == 0

Its working for me

Where Next?

Popular in Proposals: Ideas Top

sbennett33
When building a component library, it is often useful to give users the ability to customize the underlying element or component to use. ...
New
woylie
We are seeing a lot of warning logs like this: navigate event to "https://someurl" failed because you are redirecting across live_sessio...
New
azyzz228
The slow network is known to be an Achilles heel of LiveView’s architecture. Recently, I was working on creating a fast rendering map wi...
New
andreamancuso
Hey folks, This might sound niche, but I think it’s worth bringing up - especially given Phoenix’s reputation for being lightweight, por...
New
marcandre
I notice that most events have bindings (e.g. phx-keyup) but not the input event. The input event is the preferred way to interact with ...
New
engineeringdept
In 2026 double submit/session tokens are no longer necessary to prevent against CSRF attacks. Instead, we can use the Sec-Fetch-Site head...
New
sevensidedmarble
Hello all, Apologies if this has been proposed before I guess, but I have a very simple one: With the increasing importance of LV, I th...
New
BartOtten
I’d like to propose that we refrain from using the term "DeadView" as the opposite of “LiveView” and instead choose an alternative. A new...
New
spicychickensauce
I’ve previously explored what is possible today with hacks to implement view transitions in our apps: I have since created a fork to im...
New
Flo0807
Hello everyone! Phoenix LiveView v0.18 introduced the special attributes :let, :for and :if. In addition to the :if special attribute, I...
New

Other popular topics Top

Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

We're in Beta

About us Mission Statement