themesberg / themesberg/flowbite
Phoenix Liveview: Flowbite elements that require js do not work in content pushed to the client after initial page load
Nobody has claimed this yet.
- Dominant language
- HTML
- Stars
- 9.4k
- Forks
- 862
- PR merge metrics
- No merged PRs in 30d
Description
Describe the bug
Flowbite elements that require javascript do not work or stop working when added to the dom with a liveview update. This is especially uncomfortable when using async data fetching and using flowbite components (f.e. Accordions) or when you update dom elements with server pushes.
Expected behavior
Flowbite elements do always work, even when added to the dom with a liveview update.
Desktop (please complete the following information):
- OS: MacOS
- Browser: Edge
- Version 124.0.2478.51
Additional context
Example:
<.async_result :let={time_entries} assign={@time_entries}>
<:loading>
<div class="flex items-center mt-5">
<span class="loading loading-spinner loading-lg"></span> Loading time entries and combining them with gitlab issues
</div>
</:loading>
<:failed :let={_failure}>Error loading time entries or gitlab issues</:failed>
<div data-accordion="collapse" id="example-accordion">
<button data-accordion-target={"#detail-#{issue}"}>Click me</button>
<div class="hidden" id={"detail-#{issue}"}>
important informations
</div>
</div>
</.async_result>
The example-accordion's content is pushed to the client after the async data loading has finished and flowbite elements in the result will not work.
Liveview's dom hook has the ability to apply javascript to the dom elements that are pushed to the client with
let liveSocket = new LiveSocket("/live", Socket, {
params: {_csrf_token: csrfToken},
dom: {
onBeforeElUpdated(from, to) {
//copy attributes from "from" to "to" or apply function on "to"
}
}
})
But the init.... (f.e. initAccordions) functions are using document.querySelectorAll() and thus are unable to be scoped to the incoming DOM nodes.
A workaround for now is to apply a custom hook to the parent node
const Hooks = {}
Hooks.Flowbite = {
mounted(){
initFlowbite();
}
updated(){
this.mounted()
}
}
let liveSocket = new LiveSocket("/live", Socket, {
params: {_csrf_token: csrfToken}, hooks: Hooks
})
and in the template file
...
<div phx-hook="Flowbite">
<div data-accordion="collapse" id="example-accordion">
<button data-accordion-target={"#detail-#{issue}"}>Click me</button>
<div class="hidden" id={"detail-#{issue}"}>
important informations
</div>
</div>
</div>
...
This works, but triggers a lot of init functions for each update. It would be great to be able to apply the init functions to the dom elements that are updated.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the issue with the async_result example and inspect initAccordions/initFlowbite alongside LiveSocket's dom.onBeforeElUpdated hook. Compare initialization on initial markup with content pushed after the async update. Done means Flowbite elements in updated DOM content work without rerunning every init function for the whole parent.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- elixir, javascript, tailwindcss
- Domain
- frontend, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100