ppff01
DaisyUI modal in phoenix 1.8 - code example
Hi there,
I’ve been testing Phoenix 1.8 a lot lately and noticed the modal component from 1.7 had disappeared. Since we now have DaisyUI which comes with a Modal component, I figured I’d try to use it. It’s not that easy because if you want to do it the cleanest way, you need to use the browser’s showModal() function. I won’t get into too much detail but after several hours of tests here is what I came up with. I hope it can be useful to some of you here!
The component:
@doc """
Modal dialog.
"""
attr :id, :string, required: true
attr :on_cancel, JS, default: %JS{}
slot :inner_block, required: true
def modal(assigns) do
~H"""
<dialog
id={@id}
phx-hook="Modal"
phx-remove={
JS.remove_attribute("open")
|> JS.transition({"ease-out duration-200", "opacity-100", "opacity-0"}, time: 0)
}
data-cancel={JS.exec(@on_cancel, "phx-remove")}
class="modal"
>
<.focus_wrap
id={"#{@id}-container"}
phx-window-keydown={JS.exec("data-cancel", to: "##{@id}")}
phx-key="escape"
phx-click-away={JS.exec("data-cancel", to: "##{@id}")}
class="modal-box"
>
<.button phx-click={JS.exec("data-cancel", to: "##{@id}")} class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2 tx-lg">✕</.button>
<%= render_slot(@inner_block) %>
</.focus_wrap>
</dialog>
"""
end
Add a new hook which will trigger showModal():
Hooks.Modal = {
mounted() {
this.el.showModal();
},
}
In your liveview, just add the modal:
<.modal
id="delete_modal"
:if={@live_action in [:delete]}
on_cancel={JS.patch(~p"/brands")}
>
<h3 class="text-xl text-bold">Warning</h3>
<p class="text-md mt-3">Are you sure you want to delete brand {@brand.name}?</p>
<div class="modal-action">
<.button phx-click="delete" phx-value-id={@brand.id}>Delete</.button>
</div>
</.modal>
It’s basically a hot-swap solution for Phoenix modal 1.7. Please note that the modal should be opened only with a specific live_action which is defined in the router:
live "/brands/:id/delete", BrandsLive, :delete
This is necessary because we don’t have an easy way to trigger the close javascript command and retrieve data while letting the browser-operated fade out operate properly (or at least I didn’t find one). But it’s also very useful because it allows us to pass parameters to the modal easily using the url.
Anyway, just my two cents! Feel free to suggest improvements or ask questions.
Most Liked
ppff01
Update: the issue with this implementation is that if you use it with a form for example, the liveview updates will rewrite the html without the open tag on the dialog HTML element, therefore closing the modal.
So here is my final solution: ditch the hook and just include the open tag from the start.
<dialog
id={@id}
phx-remove={
JS.remove_attribute("open")
|> JS.transition({"ease-out duration-200", "opacity-100", "opacity-0"}, time: 0)
}
data-cancel={JS.exec(@on_cancel, "phx-remove")}
class="modal"
open
>
LostKobrakai
core_components only include what phoenix generators use. Modals in generated code eventually got replaced with separate pages, so there was no use for the modal anymore.
RodolfoSilva
This is my implementation:
@doc """
Renders a modal.
## Examples
<.modal id="confirm-modal">
This is a modal.
</.modal>
JS commands may be passed to the `:on_cancel` to configure
the closing/cancel event, for example:
<.modal id="confirm" on_cancel={JS.navigate(~p"/posts")}>
This is another modal.
</.modal>
"""
attr :id, :string, required: true
attr :on_cancel, JS, default: nil
slot :inner_block, required: true
def modal(assigns) do
~H"""
<.portal id={@id} target="body">
<dialog
open
closedby={if @on_cancel, do: "any", else: "none"}
phx-mounted={
JS.ignore_attributes("open")
|> JS.transition({"ease-in duration-200", "opacity-0", "opacity-100"}, time: 0)
}
phx-remove={
JS.remove_attribute("open")
|> JS.transition({"ease-out duration-200", "opacity-100", "opacity-0"}, time: 0)
}
class="modal"
>
<.focus_wrap
class="modal-box w-11/12 max-w-2xl"
id={"#{@id}-container"}
tabindex="0"
phx-key="escape"
phx-window-keydown={@on_cancel}
>
<form :if={@on_cancel} method="dialog">
<button
phx-click={@on_cancel}
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
>
✕
</button>
</form>
{render_slot(@inner_block)}
</.focus_wrap>
<form :if={@on_cancel} method="dialog" class="modal-backdrop">
<button phx-click={@on_cancel}>close</button>
</form>
</dialog>
</.portal>
"""
end
Requires the LiveView > v1.1.5.
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









