I’m trying to implement a show/edit control using nested live views. My prototype is working, but when it swaps out the show
template for the edit
template, it creates a distracting effect where the content becomes shorter then longer again. Hopefully, the following video shows what’s happening (on my local dev machine):
https://www.loom.com/share/9af57c7726104de196e7ec16e25dd148
When I click the edit button, sometimes the flash is noticeable/sometimes the flash is fast. I think this variable delay is because I’m using Stimulus to replace the textarea (in the edit
template) with an Emojionearea. When I disable the Simulus controller, the flash is consistently faster.
However, it would be nice if it didn’t do the “flash” at all. It looks like that part of the DOM is being cleared, then the browser updates the visual tree, then the new elements are added and the visual tree is updated again. So I’m now thinking maybe I’m doing it wrong?
The parent live view is called ShowOrEdit
. The child live views are Show
and Edit
. Here’s what show_or_edit.html.leex
looks like:
<div>
<%= if @edit do
live_render( @socket, ZoinksWeb.TaskLive.Edit, session: %{
id: @block.id,
user_id: @user_id,
dates: @dates
})
else
live_render( @socket, ZoinksWeb.TaskLive.Show, session: %{
id: @block.id,
user_id: @user_id
})
end %>
</div>
<div class="mt-5">
<%= live_render(@socket, ZoinksWeb.SearchLive, session: %{
blocks: [@block.id],
user_id: @user_id
}) %>
</div>
Depending on @edit
the if
statement will show one live view or the other. The 3rd live view is where the content at the bottom comes from.
Here’s part of ShowOrEdit
:
def mount( %{path_params: %{"id" => id}} = session, socket ) do
socket = socket |> assign_new( :current_user, fn -> Zoinks.Guardian.resource_from_session(session) end)
block = Blocks.get_block!(id)
edit = Map.has_key?( session, :edit ) || Map.has_key?( socket.assigns, :edit )
dates = Map.has_key?( session, :dates ) || Map.has_key?( socket.assigns, :dates )
{ :ok, assign(socket, %{
user_id: socket.assigns.current_user.id,
edit: edit,
dates: dates,
block: block
})}
end
def handle_info( :show_task, socket ) do
socket = assign( socket, %{edit: false, dates: false} )
{:noreply, live_redirect(socket, to: Routes.task_live_path(socket, ZoinksWeb.TaskLive.ShowOrEdit, socket.assigns.block), replace: true)}
end
def handle_info( :edit_task, socket ) do
socket = assign( socket, %{edit: true, dates: false} )
{:noreply, live_redirect(socket, to: Routes.task_edit_path(socket, ZoinksWeb.TaskLive.ShowOrEdit, socket.assigns.block), replace: true)}
end
To set @edit
a nested live view can do the following:
def handle_event("edit_task", _value, socket) do
send( socket.parent_pid, :edit_task )
{:noreply, socket}
end
A couple of thoughts:
- Is my approach to dynamically swapping nested live views valid? Is there a better way?
- Could I do something in my
show/edit
templates to make them more “compatible” so that diffing the DOM is more of a “merge the details” than a “clear at the root element and replace”?
I can provide more info, I was just a bit worried that the question was already too detailed…