tomthestorm
Live view renders all html in "for" loop not only part of it
Hi I try to understand LiveView . I want to expand and hide HTML after clicking button.
Here code :
use Phoenix.LiveView
@packs [
%{name: "PACK1", slug: "pack1"} ,
%{name: "PACK2", slug: "pack2"}
]
def mount(_params, _session , socket) do
{:ok, assign(socket,show_pack: false , pack_slug: nil, packs: @packs) }
end
def handle_event("open_pack", %{"pack_slug" => pack_slug}, socket) do
{:noreply, assign(socket,show_pack: true, pack_slug: pack_slug)}
end
def handle_event("close_pack", %{"pack_slug" => pack_slug} , socket) do
{:noreply, assign(socket,show_pack: false, pack_slug: pack_slug) }
end
def render(assigns) do
~L"""
<div class="container">
<%= for pack <- @packs do %>
<div class="row">
<div class="column"><b> <%= pack[:name] %> </b>
<%= if pack[:slug] == @pack_slug || @pack_slug == nil do %>
<%= if @show_pack == true do %>
<button phx-click="close_pack" phx-value-pack_slug="<%= pack[:slug] %>" > - </button>
<div class="row"> </br>
<div class="column column-offset-10"><b> <%= pack[:name] %> is open </b> </div>
</div>
<% else %>
<button phx-click="open_pack" phx-value-pack_slug="<%= pack[:slug] %>" > + </button>
<% end %>
<% end %>
</div>
</div>
<% end %>
</div>
I want to open and close many packs but keep state if pack is open or closed.
I go through “for” loop to list all packs as initial state (packs are closed) . But when I click (+) button of the first pack , LiveView goes again through whole “for” loop and hide (+) button of the second pack, forever.
I think it should modify only part of code responsible for the first pack but result is different.
Before click (+)
After click (+)
I want to modify part of HTML responsible for certain pack independently, but all code is modified.
Thanks for tip.
Maybe should I use live component ?
Marked As Solved
sfusato
Here’s the code for the 1st option:
@packs [
%{name: "PACK1", slug: "pack1", show: false},
%{name: "PACK2", slug: "pack2", show: false},
%{name: "PACK3", slug: "pack3", show: false},
%{name: "PACK4", slug: "pack4", show: false}
]
def mount(_params, _session, socket) do
{:ok, assign(socket, packs: @packs)}
end
def render(assigns) do
~L"""
<div class="container">
<%= for pack <- @packs do %>
<div class="row">
<div class="column"><b> <%= pack[:name] %> </b>
<%= if pack[:show] do %>
<button phx-click="toggle" phx-value-pack_slug="<%= pack[:slug] %>" > - </button>
<div class="row"> </br>
<div class="column column-offset-10"><b> <%= pack[:name] %> is open </b> </div>
</div>
<% else %>
<button phx-click="toggle" phx-value-pack_slug="<%= pack[:slug] %>" > + </button>
<% end %>
</div>
</div>
<% end %>
</div>
"""
end
def handle_event("toggle", %{"pack_slug" => pack_slug}, socket) do
socket =
socket
|> update(:packs, fn packs ->
Enum.map(packs, fn
%{slug: ^pack_slug} = pack -> Map.update!(pack, :show, fn state -> !state end)
pack -> pack
end)
end)
{:noreply, socket}
end
Obviously, whenever anything changes inside packs, LiveView will automatically re-render the entire list. An improvement to this implementation would be going the stateful LiveComponent way.
Also Liked
tomthestorm
Thanks a lot. Now I better understand topic. I try also do this by LiveComponent due to huge list in final implementation of code.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance











