Newer versions of LiveView added new powerful functions to the JS
module like toggle_attribute
and toggle_class
. These are great because they allow us to create more complex JS interactions without having to fallback to a hook.
I do still think that some options are missing in JS
that would make our lives easier. Mainly, I think we should have conditional functions for things like exec
, focus
, focus_first
, navigate
, patch
, pop_focus
, push
and push_focus
(maybe some other functions as-well?).
What I mean by “conditional” is having something similar to what toggle_attribute
and toggle_class
does. I will use push
as an example.
Let’s say I want to create a dropdown button, to do that I would use phx-click={JS.toggle_class("hidden", "block", to: dropdown_menu)}
in the button to show the dropdown menu and phx-click-away={JS.add_class("hidden")}
in the dropdown menu itself.
That works great until I want to make it a little more complex, let’s say I want to push an event to the server only when the dropdown menu is open. I can’t just change the button to phx-click={JS.toggle_class("hidden", "block", to: dropdown_menu) |> JS.push("load_menu_stuff")}
because if I click in the button to open the dropdown and then click on it again to close it, it will call the "load_menu_stuff"
event even though I’m actually closing the dropdown in this case.
So, what I propose is to either create a new version of these functions, ex. conditional_push
or expand the opts
argument already in place in the existing functions.
Here are some ideas on how to handle it:
# This means that the push will only happen if the target element has the class `block`
JS.conditional_push({:class, "block"}, "load_menu_stuff")
# This means that the push will only happen if the target element has the attribute `open="true"`
JS.conditional_push({:attribute, "open", "true"}, "load_menu_stuff")
or
# This means that the push will only happen if the target element has the class `block`
JS.push("load_menu_stuff", condition: [class: "block"])
# This means that the push will only happen if the target element has the attribute `open="true"`
JS.push("load_menu_stuff", condition: [attribute: {"open", "true"}])
I didn’t do a deep dive on how the current LV JS code works, but I believe that these would work pretty similar to the way toggle_attribute
and toggle_class
works (in the sense on how they verify its condition), so maybe this is not that hard to implement.