Closing a JS modal from handle_event

Hi, I’m new to Elixir so just trying to find my feet. I have a modal (from core_components.ex) open with a form in it, when the form submits, I either want to update the state to show an error, or action the form and close the modal. I’m having a bit of trouble closing the modal from the handle_event callback and wondered if anyone could give any advice. The setup that I have is…

form_live.html.eex

<:link phx-click={show_modal("form-modal")}>Open</:link>

<.modal id="form-modal">
  <form phx-submit="form-go" class="-m-4">
    <input name="name" placeholder="Name" value={@form["name"]} />
    <button type="submit" class="btn-primary">Go</button>
  </form>
</.modal>

form_live.ex

defmodule App.FormLive do
  use App, :live_view

  def handle_event("form-go", form, socket) do

    # Do some validation
    updated_form = case did_validate?(form) do
      true ->
        # Close the modal
        # JS.hide_modal("form-modal")
        do_some_stuff_with_name(form["name"])
        %{"name" => ""}
    end
    {:noreply, assign(socket, :form, updated_form)}
  end
end

Obviously, the hide_modal call fails, but that’s what I’d ideally like to do. Is that even possible?

If not, the other thing that I’ve been thinking about is managing the open state from the server and pushing that down in the state, but it feels like because core_components already provides most of the modal functionality it would be a duplication of effort.

Thanks in advance, I’ve been scratching my head on this for a while

Phoenix.LiveView.JS is a collection of functions that provides solutions to common client-side needs (like showing/hiding modals).
The first example in the docs goes over modals so hopefully that points you in the right direction.

This thread has good info about possible approaches

Thanks! @JohnnyCurran the phx:js-exec method seems to work well, so changing the code to be

form_live.html.eex

<.modal id="form-modal" data-cancel={hide_modal("form-modal")}>
...
</.modal>

form_live.ex

def handle_event("form-go", form, socket) do
  {:noreply, push_event(socket, "js-exec", %{to: "#form-modal", attr: "data-cancel"})}
end

and the added JS handler

window.addEventListener("phx:js-exec", ({detail}) => {
  document.querySelectorAll(detail.to).forEach((el) => {
    liveSocket.execJS(el, el.getAttribute(detail.attr))
  })
})

…seems to work well. Thanks :+1:

1 Like