This is vaguely similar to what I’m experiencing. Here’s a minimal project that reproduces the issue. I’d be grateful if anyone can take a look.
The issue is that when
A) rendering directly from a LiveView, everything works. But when
B) the rendering is moved to a LiveComponent, Alpine’s x-show
(or something else) “breaks”: the search results still show, but not immediately on typing (only after users also presses the Enter key).
In other words, moving the render/1
shown below (as well as some few handle_event/3
) from
- A) a
:live_view
to - B) a
:live_component
somehow breaks and Alpine’s x-show
doesn’t show the <ul>
immediately. It does show up but only if one also then presses say the Enter key, or defocuses-and-refocuses the search <input>
with a mouse, etc.
def render_from_live_view(assigns) do
~H"""
<div class="flex-col relative" x-data="{open: false}">
<p>Using LiveView, the result will immediately show below, without needing to also press Enter key.</p>
<form id="search-form" phx-submit="submit">
<input
placeholder="type 'a'"
name="search-input"
id="search-input"
class="block border border-black"
phx-change="search"
phx-hook="FocusOnMounted"
x-on:focus="open = true"
/>
</form>
<ul :if={@search_results != []} x-show="open">
<li :for={result <- @search_results}><%= result %></li>
</ul>
</div>
"""
end
And the relevant lines from app.js
:
import Alpine from "alpinejs"
window.Alpine = Alpine
Alpine.start()
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
params: {_csrf_token: csrfToken},
hooks: {
FocusOnMounted: {
mounted() {
this.el.focus();
}
},
},
dom: {
onBeforeElUpdated(from, to) {
if (from._x_dataStack) {
window.Alpine.clone(from, to);
}
}
}
})
Visually, I only see the following differences in the relevant HTML between the :live_view
(left) and the :live_component
(right):
Other somewhat related threads: