New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Overriding the default confirm behaviour - Make the example work #3225
Conversation
Add a working example of customising the `data-confirm` behaviour
Which part about the existing docs didn't work? Also please keep type="button". Thanks! |
Click handler events are not asynchronous, so you need to cancel or allow the event straight away. The example should have never worked as no other methods can pause javascript executions like We need to have a workaround, like my re-trigger example here. Here's the more in depth article |
@chrismccord not sure if need to ping at all, the ci seems to fail for no reason. |
More comments and small indent changes
Working single file script with these changes: Application.put_env(:sample, Example.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 5001],
server: true,
live_view: [signing_salt: "aaaaaaaa"],
secret_key_base: String.duplicate("a", 64)
)
Mix.install([
{:plug_cowboy, "~> 2.5"},
{:jason, "~> 1.0"},
{:phoenix, "~> 1.7"},
# please test your issue using the latest version of LV from GitHub!
{:phoenix_live_view, github: "phoenixframework/phoenix_live_view", branch: "main", override: true},
])
# build the LiveView JavaScript assets (this needs mix and npm available in your path!)
path = Phoenix.LiveView.__info__(:compile)[:source] |> Path.dirname() |> Path.join("../")
System.cmd("mix", ["deps.get"], cd: path, into: IO.binstream())
System.cmd("npm", ["install"], cd: Path.join(path, "./assets"), into: IO.binstream())
System.cmd("mix", ["assets.build"], cd: path, into: IO.binstream())
defmodule Example.ErrorView do
def render(template, _), do: Phoenix.Controller.status_message_from_template(template)
end
defmodule Example.HomeLive do
use Phoenix.LiveView, layout: {__MODULE__, :live}
def mount(_params, _session, socket) do
{:ok, assign(socket, :count, 0)}
end
def render("live.html", assigns) do
~H"""
<script src="/assets/phoenix/phoenix.js"></script>
<script src="/assets/phoenix_live_view/phoenix_live_view.js"></script>
<script src="/assets/phoenix_html/phoenix_html.js"></script>
<script src="https://unpkg.com/vex-js@4.1.0/dist/js/vex.combined.min.js"></script>
<script>vex.defaultOptions.className = 'vex-theme-os'</script>
<link rel="stylesheet" href="https://unpkg.com/vex-js@4.1.0/dist/css/vex.css">
<link rel="stylesheet" href="https://unpkg.com/vex-js@4.1.0/dist/css/vex-theme-os.css">
<%!-- uncomment to use enable tailwind --%>
<%!-- <script src="https://cdn.tailwindcss.com"></script> --%>
<script>
// Compared to a javascript window.confirm, the custom dialog does not block
// javascript execution. Therefore to make this work as expected we store
// the successful confirmation as an attribute and re-trigger the click event.
// On the second click, the `data-confirm-resolved` attribute is set and we proceed.
const RESOLVED_ATTRIBUTE = "data-confirm-resolved";
// listen on document.body, so it's executed before the default of
// phoenix_html, which is listening on the window object
document.body.addEventListener('phoenix.link.click', function (e) {
// Prevent default implementation
e.stopPropagation();
// Introduce alternative implementation
var message = e.target.getAttribute("data-confirm");
if(!message){ return; }
// Confirm is resolved execute the click event
if (e.target?.hasAttribute(RESOLVED_ATTRIBUTE)) {
e.target.removeAttribute(RESOLVED_ATTRIBUTE);
return;
}
// Confirm is needed, preventDefault and show your modal
e.preventDefault();
e.target?.setAttribute(RESOLVED_ATTRIBUTE, "");
vex.dialog.confirm({
message: message,
callback: function (value) {
if (value == true) {
// Customer confirmed, re-trigger the click event.
e.target?.click();
} else {
// Customer canceled
e.target?.removeAttribute(RESOLVED_ATTRIBUTE);
}
}
})
}, false);
let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket)
liveSocket.connect()
</script>
<style>
* { font-size: 1.1em; }
</style>
<%= @inner_content %>
"""
end
def render(assigns) do
~H"""
<%= @count %>
<button phx-click="inc" data-confirm="oh!">+</button>
<button phx-click="dec">-</button>
"""
end
def handle_event("inc", _params, socket) do
{:noreply, assign(socket, :count, socket.assigns.count + 1)}
end
def handle_event("dec", _params, socket) do
{:noreply, assign(socket, :count, socket.assigns.count - 1)}
end
end
defmodule Example.Router do
use Phoenix.Router
import Phoenix.LiveView.Router
pipeline :browser do
plug(:accepts, ["html"])
end
scope "/", Example do
pipe_through(:browser)
live("/", HomeLive, :index)
end
end
defmodule Example.Endpoint do
use Phoenix.Endpoint, otp_app: :sample
socket("/live", Phoenix.LiveView.Socket)
plug Plug.Static, from: {:phoenix, "priv/static"}, at: "/assets/phoenix"
plug Plug.Static, from: {:phoenix_live_view, "priv/static"}, at: "/assets/phoenix_live_view"
plug Plug.Static, from: {:phoenix_html, "priv/static"}, at: "/assets/phoenix_html"
plug(Example.Router)
end
{:ok, _} = Supervisor.start_link([Example.Endpoint], strategy: :one_for_one)
Process.sleep(:infinity) |
Thank you! 🙌🏻 I'll try to add an e2e test for this separately. |
Add a working example of overriding the
data-confirm
behaviour