How to close modal

I don’t know how to close a form that is opened in a Modal. How do I do that?

Here is a spike test example that opens a modal:

defmodule AppWeb.SandboxLive do
   use AppWeb, :live_view  
   def mount(_params, _session, socket)  do
	 {:ok, assign(socket, greeting: "Hello World")}  
   end

   def handle_event("open-modal", params, socket)do  
       IO.inspect "We are the world"
      {:noreply, socket}

   end

   def handle_event("submit-modal-form", params, socket) do 
          IO.inspect params
            {:noreply, socket}
   end



   def render(assigns) do
	   ~H"""

	  <.modal id={"modal-id"}>

		<%= assigns.greeting %>

		<form phx-submit ={"submit-modal-form"}>
         <input type="text" /> 
         <input type = "submit"/>

		</form>

    	</.modal>

	<button phx-click = {show_modal("modal-id")}>CLICK to Open Modal</button>

	   """ 
   end
end

The documentation is linked below and it looks like it explains how to do this, but I don’t understand it.
Documentation isn’t clear.

https://hexdocs.pm/phoenix_live_view/bindings.html#js-commands

I tried to hodgepodge their code with mine to create a working example.


defmodule AppWeb.SandboxLive do
   use AppWeb, :live_view  
   def mount(_params, _session, socket)  do
	 {:ok, assign(socket, greeting: "Hello World")}  
   end

   def handle_event("open-modal", params, socket)do  
       IO.inspect "We are the world"
      {:noreply, socket}

   end

   def handle_event("submit-modal-form", params, socket) do 
          IO.inspect params
            {:noreply, socket}
   end

	def hide_modal(js \\ %JS{}, selector) do
	  js
	  |> JS.push("modal-closed")
	  |> JS.remove_class("show", to: selector, transition: "fade-out")
	end


   def render(assigns) do
	   ~H"""

	  <.modal id={"modal-id"}>

		<%= assigns.greeting %>

		<form phx-submit ={"submit-modal-form"}>
         <input type="text" /> 
         <input type = "submit" phx-click={hide_modal("#modal")}/>

		</form>

    	</.modal>

	<button phx-click = {show_modal("modal-id")}>CLICK to Open Modal</button>

	   """ 
   end
end

I get an error that says

CompileError

lib/app_web/live/sandbox.ex:18: imported AppWeb.CoreComponents.hide_modal/1 conflicts with local function

Console output is shown below.

Compiling 1 file (.ex) == Compilation error in file lib/app_web/live/sandbox.ex == ** (CompileError) lib/app_web/live/sandbox.ex:18: imported AppWeb.CoreComponents.hide_modal/1 conflicts with local function

I don’t know what that means

This thread might be helpful:

https://elixirforum.com/t/how-to-toggle-phoenix-modal-component-show-hide/54313/7?u=brads2s

For anyone searching for this.

This seems to work:
JS.navigate(~p"/")

Example:

	   <.modal id={"modal-id"}>

		   <%= assigns.greeting %>

			<form phx-submit ={"submit-modal-form"}>
	         <input type="text" /> 
	         <input phx-click={JS.navigate(~p"/")}  type = "submit" />
			</form>

    	</.modal>

I’m sorry I didn’t realize you were OP. You are saving lots of future work for others I’m sure.

The compile error is saying that the AppWeb.CoreComponents.hide_modal/1 conflicts with the hide_modal/1 function that you’ve defined in the AppWeb.SandboxLive module. The call to use AppWeb, :live_view at the top of the AppWeb.SandboxLive module will import all the functions from the AppWeb.CoreComponents module into your module, and since both modules implement a function with the same name and same number of arguments, this creates a compiler error.

You can either remove your hide_modal/1 function from the AppWeb.SandboxLive module, or rename the function, in order to make the code compile again. And then you should be able to hide your modal with the AppWeb.CoreComponents.hide_modal/1 function with code like this:

  def render(assigns) do
    ~H"""
    <.modal id={"modal-id"}>
      <%= assigns.greeting %>
      <form phx-submit ={"submit-modal-form"}>
        <input type="text" /> 
        <input type = "submit" phx-click={hide_modal("modal-id")}/>
      </form>
    </.modal>

    <button phx-click = {show_modal("modal-id")}>CLICK to Open Modal</button>
    """ 
  end
1 Like

Whoops, sorry deleting this reply – I meant to reply to the thread :slight_smile:

Here’s how to close a CoreComponent modal from server side:


Say for example we have this modal:

<.modal id="some-modal-id">Some text</.modal>

Notice the phx-remove attr in the default CoreComponents modal:

def modal(assigns) do
  ~H"""
  <div
    id={@id}
    phx-mounted={@show && show_modal(@id)}
    phx-remove={hide_modal(@id)} <-- **this is what we want to trigger**
    data-cancel={JS.exec(@on_cancel, "phx-remove")}
    ...
  >
  """
end

To trigger that phx-remove attr/command to close the modal from the server (e.g. in handle_event/3), we can use push_event/3.

Add this window event listener to your app.js (which allows triggering a JS command from the server side):

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

From: https://fly.io/phoenix-files/server-triggered-js/

Finally, use push_event/3 from the server like so:

def handle_event("close-modal", _, socket) do
  {:noreply,
    push_event(socket, "js-exec", %{
      to: "#some-modal-id",
      attr: "phx-remove"
    })}
end

This will run that client side event listener which then triggers the phx-remove on the modal.

3 Likes

Perfect, thanks!