How to close modal in event handler liveview

A few hours of work as not made it happen :frowning:. I need approach 1 (or similar) to work.

Note: I’m using the default LiveHelpers modal code.1

Approach 1

The modal should close on “save okay” only, so is handled after save in the handler. For now handle_event just tries to close. hide_modal runs but does not close the modal.

  def handle_event("save", %{"user" => user_params}, socket) do
    IO.inspect(user_params) # this runs
    save_user(socket, socket.assigns.action, user_params)
    MyApp.LiveHelpers.hide_modal() #default location of modal
    {:noreply, socket}
  end

# form where the save button is
  <.form
    let={f}
    for={@changeset}
    id="user-form"
    phx-target={@myself}
    phx-change="validate"
    phx-submit="save">
  
    <div>
      <%= submit "Save", phx_disable_with: "Saving..."%>
    </div>
  </.form>

# modal close function comes w/ Phoenix, and works elsewhere
  def hide_modal(js \\ %JS{}) do
    IO.puts("fired")
    js
    |> JS.hide(to: "#modal", transition: "fade-out")
    |> JS.hide(to: "#modal-content", transition: "fade-out-scale")
  end

IO.puts("fired") runs but the modal does not close.

Approach 2

Adding to the button does work but it closes on any click, even w/ a blank form, or w/ errors. This would work for a cancel button but not for save.

 <%= submit "Save", phx_disable_with: "Saving...", phx_click: MyApp.LiveHelpers.hide_modal()%>
  

I guess it’s something to do w/ the binding phx- events directly to the button render, or lack thereof. Since it’s conditional to “save okay” this blunt click binding will not suffice.

If you navigate away, or patch the url so there is another live_action, this should happen automatically.

<.modal
  :if={@live_action in [:new, :edit]}
  id="task-modal"
  show
  on_cancel={JS.navigate(~p"/tasks")}
>
  <:title><%= @page_title %></:title>
  <.live_component
    module={MyAppWeb.TasksLive.FormComponent}
    id={@task.id || :new}
    title={@page_title}
    action={@live_action}
    task={@task}
    navigate={~p"/tasks"}
  />
</.modal>

But If you dont change url, or live_action,you can update it so instead of:

:if={@live_action in [:new, :edit]}

you can use set a show_modal in assigns and let that control it.

:if={@show_modal}

Otherwise, I usually go for a variant described here:

1 Like

The modal is already showing. The problem is I cannot close it.
Oh you are saying the navigate might work…

Default modal

def modal(assigns) do
    assigns = assign_new(assigns, :return_to, fn -> nil end)

    ~H"""
    <div id="modal" class="phx-modal fade-in" phx-remove={hide_modal()}>
      <div
        id="modal-content"
        class="phx-modal-content fade-in-scale"
        phx-click-away={JS.dispatch("click", to: "#close")}
        phx-window-keydown={JS.dispatch("click", to: "#close")}
        phx-key="escape"
      >
        <%= if @return_to do %>
          <%= live_patch "✖",
            to: @return_to,
            id: "close",
            class: "phx-modal-close",
            phx_click: hide_modal()
          %>
        <% else %>
          <a id="close" href="#" class="phx-modal-close" phx-click={hide_modal()}>✖</a>
        <% end %>

        <%= render_slot(@inner_block) %>
      </div>
    </div>
    """
  end

Yeah, in the example above, when leaving :new and going back to :index. the modal closes.