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.

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

tubedude
Hey, Earlier i posted a question, but after some research I think a proposal is due. Working on a Phoenix Live View app, I needed clien...
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
mythicalprogrammer
Hello, since the 1.8rc0 was out, DaisyUI was noted to have the benefit of light and dark mode. From the elixir subreddit, it seem a few ...
New
pelopo
Hi, Oficial docs got this wonderful feature to download the ePub version that we can chuck to our kindles and read it. Now we are in the...
New
mortenlund
Hi! I would like to suggest a new callback in the lifecycle of the Live Component which is unmount. Sometimes it is nice to be able to ...
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
victor23k
Hi all! I come with a proposal to create LiveViews inside the shadow DOM. At Doofinder, we have experience building embeddable LiveView a...
New
ffloyd
The Problem Currently, if I define a struct in the following way: defmodule MyStruct do # Both x and y will have the FIXED values unti...
New
calebjosue
I am reading (Once more) the Phoenix web framework documentation, in the From endpoint to views section contained in the Request life-cyc...
New
lemaster
Currently we have stream_insert and stream that can either add new items to a stream or update existing items if they are already present...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41454 115
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30840 112
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
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
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New

We're in Beta

About us Mission Statement