Closing a Modal only after server side operation was successful

For a modal with a form to change the user name, there are basically two scenarios when submitting the form:

  1. The validation was successful → The server persists the name change → The modal is no longer needed
  2. 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