I am trying to use the main branch of phoenix liveview to test the new reset feature on streams
In my mix.exs I have
{:phoenix_live_view,
github: "phoenixframework/phoenix_live_view", branch: "main", override: true}
I have created a simple liveview example for a phonebook.
I have a list of names and an index at the top. Clicking on the index should load the contacts that start with the Index letter.
Whenever the index letter is clicked, i load all the relevant contacts, put them in the stream and set the reset flag to true.
This is working fine.
The Problem
Clicking on each contact opens up a modal with the contact details.
This works fine for the first time. Click on the modal, the modal appears.
But whenever the stream gets reset (after clicking a index letter), clicking on the contact brings up the modal, but the stream gets cleared and all contacts disappear.
I am not able to figure out why this is happening.
I tried to replace the stream, with normal assigns and everything works like it should (the modal appears and the contacts do not get cleared).
Can some one please advise as to why is this happening?
Full code is below
defmodule StreamsTestWeb.UserLive.Index do
use StreamsTestWeb, :live_view
@impl true
def render(assigns) do
~H"""
<div class="h-20 flex items-center space-x-12 border-b border-zinc-200 px-4 lg:px-8">
<div class="text-lg font-bold">Users</div>
</div>
<div class="tabs flex items-center gap-2 text-xs font-medium px-r lg:px-8 mt-8">
<.tab :for={tab <- @tabs} tab={tab} selected_tab={@selected_tab} />
</div>
<div :if={@term != ""} class="flex items-center gap-4 text-sm px-4 lg:px-8 mt-6">
<div class="text-zinc-500">Searching <em class="font-bold text-zinc-700"><%= @term %></em></div>
<button
type="button"
class="flex items-center gap-2 text-xs text-zinc-400 hover:text-zinc-600"
phx-click="clear_search"
>
<.icon name="hero-x-circle" class="w-5 h-5" />
</button>
</div>
<div class="px-4 lg:px-8">
<.table
id="users"
rows={@streams.users}
row_click={fn {_id, u} -> JS.push("show_user", value: %{id: u.id}) end}
>
<:col :let={{_id, s}} label="Name">
<%= s.name %>
</:col>
</.table>
</div>
<div id="modal_box">
<.modal :if={@user} id="detail-modal" show on_cancel={JS.push("hide_user")}>
<.header><%= @user.name %></.header>
</.modal>
</div>
"""
end
@impl true
def mount(_params, _session, socket) do
socket =
socket
|> assign(:tabs, get_tabs())
|> assign(:selected_tab, "All")
|> assign(:term, "")
|> assign(:user, nil)
|> stream(:users, get_users)
{:ok, stream(socket, :users, [])}
end
attr(:tab, :string, required: true)
attr(:selected_tab, :string, required: true)
def tab(assigns) do
~H"""
<div
class={[
"w-6 h-6 inline-flex items-center justify-center rounded cursor-pointer",
@tab == @selected_tab && "text-green-700 bg-green-100",
@tab != @selected_tab && "text-zinc-500 bg-transparent hover:text-zinc-700 hover:bg-zinc-100"
]}
phx-click="load_contacts"
phx-value-index={@tab}
>
<%= @tab %>
</div>
"""
end
@impl true
def handle_event("load_contacts", %{"index" => index}, socket) do
users = get_users() |> Enum.filter(fn s -> String.starts_with?(s.name, index) end)
socket =
socket
|> assign(:selected_tab, index)
|> assign(:term, "")
|> assign(:user, nil)
|> stream(:users, users, reset: true)
{:noreply, socket}
end
def handle_event("show_user", %{"id" => id}, socket) do
socket =
assign(
socket,
:user,
Enum.find(get_users, fn u -> u.id == id end)
)
{:noreply, socket}
end
def handle_event("hide_user", _params, socket) do
socket =
assign(
socket,
:user,
nil
)
{:noreply, socket}
end
defp get_tabs() do
[
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z"
]
end
def get_users() do
[
%{id: 1, name: "A Name"},
%{id: 2, name: "B Name"},
%{id: 3, name: "C Name"},
%{id: 4, name: "D Name"},
%{id: 5, name: "E Name"},
%{id: 6, name: "F Name"},
%{id: 7, name: "G Name"},
%{id: 8, name: "H Name"},
%{id: 9, name: "I Name"},
%{id: 10, name: "J Name"}
]
end
end