Eiji
Phoenix.LiveView.JS does not respect transition time option and calls `JS.push/1` immediately
Hi, here is an example script:
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(
jason: "~> 1.0",
phoenix: "~> 1.7.0",
phoenix_live_view: "~> 0.20.0",
plug_cowboy: "~> 2.5"
)
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}
alias Phoenix.LiveView.JS
def mount(_params, _session, socket) do
{:ok, assign(socket, :count, 0)}
end
defp phx_vsn, do: Application.spec(:phoenix, :vsn)
defp lv_vsn, do: Application.spec(:phoenix_live_view, :vsn)
def render("live.html", assigns) do
~H"""
<script src={"https://cdn.jsdelivr.net/npm/phoenix@#{phx_vsn()}/priv/static/phoenix.min.js"}></script>
<script src={"https://cdn.jsdelivr.net/npm/phoenix_live_view@#{lv_vsn()}/priv/static/phoenix_live_view.min.js"}></script>
<script>
let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket)
liveSocket.connect()
</script>
<style>
</style>
<%= @inner_content %>
"""
end
def render(assigns) do
~H"""
<button class="fadeIn" phx-click={
JS.toggle_class("fadeIn fadeOut", time: 5000)
|> JS.push("example")
}>Example</button>
"""
end
def handle_event("example", _params, socket) do
IO.inspect(Time.utc_now())
{:noreply, socket}
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(Example.Router)
end
{:ok, _} = Supervisor.start_link([Example.Endpoint], strategy: :one_for_one)
Process.sleep(:infinity)
The problem is that JS.push/1 is called immediately without giving a chance for animation. Using this example you can reproduce the issue (see inspected time in logs).
I have no idea if it’s a bug or intended behaviour. All I know is that at the start of every handle_event/3 function body For now instead of inspecting time I’m adding: Process.sleep/1 call which looks like a workaround, but after adding it animation works as expected. I wonder if other people experienced the same and if so what did you do with that. Is Process.sleep/1 call good here or maybe you can recommend some alternative (within JS + handle_event/3).
Most Liked
chrismccord
This is as designed. You want the interaction to happen as soon as possible, and the UI feedback from the animation can mask the latency. What the :time does is locks the UI from patching over the animation for that duration, so you don’t end up stomping on your animations. You definitely don’t want to sleep in your handlers. The animation time should almost always be 200-300ms and not much more. If you are trying to lock the UI for long periods because you are doing some heavily async work, consider using <.async_result> and handling loading states, but it’s not clear what your real app goals are so hard to say.
Lucassifoni
For that kind of feature I would make the button transitionable after the first render, and its visibility would depend on UI state.
Change the state to reflect the fact work has started (and this hides the button), and when you hear back from the background work, change the state to reflect that work is done.
If you see the UI as the result of a pure function over a state, a bit like Elm does it, it’s way easier than juggling with timings. Of course we also have JS to do small bits of this client-side for optimistic updates, but this shouldn’t cover “how long does work run for”.
Process.sleep will freeze this specific liveview process for the connected user for the duration of the sleep and is to be avoided.
Popular in Questions
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
- #performance










