For a modal with a form to change the user name, there are basically two scenarios when submitting the form:
- The validation was successful → The server persists the name change → The modal is no longer needed
- The validation fails → Errors are displayed on the form → The modal is still required so the user can fix the error
This means the backend result decides wether the modal can be closed or not.
I could add an event listener on the window in app.js
, dispatch an event in the backend on successful validation/persistance and close a specific or all modals on receiving the event.
But I was wondering if there is a way to tell the frontend to close the modal using hide_modal/2
function from the core components instead of adding a second variant to app.js
.
LiveView version is 0.19.5
The easiest way is to annotate the socket with push_navigate
and reload the same view they’re already on.
case perform_action(params) do
{:ok, _} ->
socket
|> put_flash(:info, "Saved new info successfully!")
|> push_navigate(to: ~p"/the/route")
{:error, changeset} ->
assign(socket, :form, to_form(changeset)}
end
https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#push_navigate/2
3 Likes
Here’s an alternative that takes advantage of LiveView’s state management and element bindings:
<button phx-click={JS.push("edit")}>Edit</button>
<.modal
id="user-form"
phx-mounted={show_modal()}
phx-remove={hide_modal()}
:if={@form}></.modal>
In your LiveView:
def handle_event("edit", _params, socket) do
{:noreply, assign(socket, :form, build_form())}
end
def handle_event("update", %{"user" => user_params}, socket) do
# after update
{:noreply, assign(socket, :form, nil)}
end
Setting/unsetting the form will show and hide the modal, and you can use phx-mounted
and phx-remove
to handle CSS transitions (LiveView has some magic to wait until the transitions have completed before removing the element from the DOM)
2 Likes
I went with a different approach as outlined in this fly.io article.
The idea is basically to
- embed some JS as an attribute on the DOM element
- push an event from the backend to trigger that JS, passing the target DOM and attribute
- Have an event listener on the frontend (app.js) that searches the DOM element, finds the attribute and executes the embedded JS.