taro

taro

I’m trying to use native dialog tag for modal. It removes much boilerplating for modal functions. Instead of JS.show/1, it uses HTMLDialogElement.showModal method.

Problem is that it doesn’t survive LiveView update. If it has phx-update="ignore", LiveView destroys native modal setup and append it to its markup position, leaving it dangling out of its place.

Is there any possibility to correct this?


Native modal activated by this.showModal()


Modal destroyed by LiveSocket


Modal appended (to an awkward place) by LiveSocket with phx-update="ignore".

Showing Posts 1 to 10

tomkonidas

tomkonidas

Can you show us your code?

The phx-update="ignore" should be on a surrounding html element and not the actual thing you want to not be affected.

So if you had:

<dialog phx-update="ignore">
  ...
</dialog>

change it to something like:

<div phx-update="ignore">
  <dialog>
    ...
  </dialog>
</div>
taro

taro OP

Tried both ways, to a container and to itself. JS.dispatch makes the same result too.

<.button onclick="document.querySelector('#continue-with-email-modal').showModal()">
  Continue with Email
</.button>
<.modal id="continue-with-email-modal">
  <.form for={:account} phx-submit="mail_access" class="space-y-4">
    <.input name="email" placeholder="Your email" />
    <.button phx-disable-with>Send Link</.button>
  </.form>
</.modal>
def handle_event("mail_access", %{"email" => email}, socket) do
  Feder.Auth.mail_access(email)

  {:noreply, put_flash(socket, :info, "Check your email")}
end
# components

  @doc """
  Renders a button.
  """
  attr :rest, :global, include: ~w(disabled form name value)

  slot :inner_block, required: true

  def button(assigns) do
    ~H"""
    <button
      class={[
        theme(),
        text_box(:double),
        tickle(),
        "font-display font-semibold italic text-base cursor-default",
        "phx-submit-loading:opacity-75"
      ]}
      {@rest}
    >
      <%= render_slot(@inner_block) %>
    </button>
    """
  end

  @doc """
  Renders a modal.

  Open with `#showModal()`.
  """
  attr :id, :string, required: true
  attr :class, :list, default: []

  slot :inner_block, required: true

  # TODO: Make is survive LiveSocket updates.
  def modal(assigns) do
    ~H"""
    <dialog
      id={@id}
      class={["bg-inherit p-0", @class]}
      onclick="if (event.target == this) this.close()"
    >
      <.focus_wrap id={"#{@id}-focus-wrap"}>
        <div class={[
          theme(),
          box(),
          "mx-auto min-w-[16rem] w-1/2 max-w-prose",
          "grid justify-items-center gap-4",
          @class
        ]}>
          <%= render_slot(@inner_block) %>
        </div>
      </.focus_wrap>
    </dialog>
    """
  end

  @doc """
  Renders an input.
  Field association generates ID, name, and value out of schema.
  Otherwise, a name must be given.

  ## Examples

      <.input field={{f, :email}} type="email" />
      <.input name="my-input" errors={["oh no!"]} />
  """
  attr :field, :any, doc: "a %Phoenix.HTML.Form{}/field name tuple, for example: {f, :email}"
  attr :name, :string
  attr :class, :list, default: []
  attr :rest, :global, include: ~w(autocomplete disabled form max maxlength min minlength
                                   pattern placeholder readonly required size step)
  slot :inner_block

  def input(assigns) do
    with assigns <- maybe_field(assigns) do
    ~H"""
      <input
        id={assigns[:id] || @name}
        name={@name}
        value={assigns[:value]}
        phx-feedback-for={@name}
        class={[theme(), text_box(), "w-full", @class]}
        {@rest}
      />
    """
    end
  end
tomkonidas

tomkonidas

Why not handle it in the liveview?

<.button phx-click="continue-with-email">
  Continue with Email
</.button>

<%= if @continue_with_email? do %>
  <.modal id="continue-with-email-modal">
    <.form for={:account} phx-submit="mail_access" class="space-y-4">
      <.input name="email" placeholder="Your email" />
      <.button phx-disable-with>Send Link</.button>
    </.form>
  </.modal>
<% end %>

In your LV:

def mount(_params, _session, socket) do
  {:ok, 
    socket
    ...
    |> assign(:continue_with_email?, false)}
end

...

def handle_event("continue-with-email", _unsigned_params, socket) do
  assign(socket, :continue_with_email?, true)
end
taro

taro OP

Native modal needs to be activated by HTMLDialogElement.showModal method. It loses modal setup otherwise.

Difference here is, Phoenix ships with hardcoded modal component, but instead I’m trying to use native modal dialog.

tomkonidas

tomkonidas

In app.js:

window.addEventListener("phx:open-dialog", (e) => {
  document.querySelector(e.detail.target_selector).showModal();
});

In your Heex:

<.button phx-click="continue-with-email">
  Continue with Email
</.button>

<div phx-update="ignore">
  <.modal id="continue-with-email-modal">
    <.form for={:account} phx-submit="mail_access" class="space-y-4">
      <.input name="email" placeholder="Your email" />
      <.button phx-disable-with>Send Link</.button>
    </.form>
  </.modal>
</div>

In your LV:

def handle_event("continue-with-email", _unsigned_params, socket) do
  push_event(socket, "open-dialog", %{target_selector: "#continue-with-email-modal"})
end

If you get that working, it still will erase your diag when the socket updates from something else? Just so I can understand the issue more.

taro

taro OP

Same result, because your code has phx-update="ignore", the modal gets appended to its markup position, instead of just disappearing.

tomkonidas

tomkonidas

What is the end result? Are you trying to close the modal? make it disappear? or persist it until the user closes it manually?

If it is to close it:

Usage notes

  • <form> elements can close a <dialog> if they have the attribute method="dialog" or if the button used to submit the form has formmethod="dialog" set. In this case, the state of the form controls are saved, not submitted, the <dialog> closes, and the returnValue property gets set to the value of the button that was used to save the form’s state.
taro

taro OP

So, on Send Link, data is submitted and LV updates, removing the modal by accident.

No. My goal here is modal not disappearing, because I could display error or do some multi-step communication.


@tomkonidas solution after submit


Ideal state after submit (No change on client, basically)

tomkonidas

tomkonidas

I have not used dialog yet, have looked into it a bit but I would have thought this should work. Is there a repo I can pull down and try stuff out for myself? Or something that reproduces the error in a minimal way.

taro

taro OP

UPDATE

Thanks for the support!

It turns out you are right. dialog works as expected with phx-update="ignore".

At the end of rabbit-holes, I’ve found out that iterating @flash map was causing all the problem. This is seemingly irrelevant. The flash is not nested, not even close to my dialog element.

I still don’t understand this behaviour.

UPDATE

I take that back. Any DOM update from LiveSocket that precedes a dialog[phx-update=ignore], will break it.

Aside from this bug, I think I shoud be able to save the status of the modal on session. So I will try to work this way.

https://github.com/phoenixframework/phoenix_live_view/issues/2433

— All posts loaded —

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
jaybe78
Hello, I’m developing a online persistent chat system (what’s app) like using elixir/dynamodb/aws for a mobile app(flutter). The diffic...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
matt-savvy
Anyone here using Honeybadger? My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of Bandit.HTTPError...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New
webofbits
Aludel - LLM Evaluation Workbench Aludel is an embeddable Phoenix LiveView dashboard for evaluating and comparing LLM prompts across mult...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews