Is there a way to persist the stimulus controller while moving an element on a page?
I have hundreds of rows in a table, and each row has a few stimulus controllers with some complex connection logic. I want to add column sorting to this table, where clicking a column header changes the order. To do this I call appendChild to move the children around. This causes hundreds of controllers to reconnect even though they don’t do anything new.
Anytime the controller is disconnected from the DOM
A disconnected controller may become connected again at a later time.
When this happens, such as after removing the controller’s element from the document and then re-attaching it, Stimulus will reuse the element’s previous controller instance, calling its connect() method multiple times.
Unfortunately initialize won’t work for me since I need access to the DOM element to set things up.
I understand it’s reconnecting because the element is being removed and then added, however I wish there was a way to tell Stimulus to persist the connection since the DOM element isn’t changing, just its location.
There are a couple work arounds I’ve found.
Add a boolean value to skip connection setup after it’s already connected. This means it still reconnects the controller which is a little overhead.
Use flexbox and order to sort rows. This isn’t compatible with tables, but it persists the elements so its very fast.
They have their drawbacks but I think it’s acceptable for my cases.
There shouldn’t be any difference between initialize() and connect() in that regard. Stimulus literally just calls both of those methods the first time a controller element appears in the DOM. You can think of initialize() as “first connect” if that helps.
Yes, you can use CableReady’s set_dataset_property operation to update the mapping object dynamically. As the README explains, it means you can provide users with a UI to set up their own custom mappings for your app, and update them in real-time thanks to StimulusReflex Nothing Morphs and CableReady.
It’s been a while since I built this, man - I suggest that you try out the code locally and experiment with the console log to satisfy your curiousity.
My quick read of the code is that the 3rd party library did not want to be unbinded if it’s not already set up, so I could use bindingsValueChanged to establish an initial value that would be ready when connect ran. And later, if the value does change, all mappings are unbound before the mappings are updated and new bindings are created. Still, memory is fuzzy and that could be only part of the story. The important part is that having an event fire in between initialize and connect is something that might allow you to design your solution far more efficiently than others might realize is possible - and that is a productivity arbitrage for you.
Seriously, though: me explaining it is like, 1/5th as good as actually trying it out yourself. As you build more and more Stimulus controllers, this sort of thing comes up just often enough to be annoying and having this technique in your pocket is like knowing how to use a flint and steel if you’re stranded in the woods.
@tleish The valueChanged callbacks can be super helpful. I’ve been using them to have controllers speak to other controllers. Inside of the first controller I’ll change a data value on a second element so that the second controller will have its valueChanged listener triggered and its just gotten data passed to it through data value.