I’m currently researching Hotwire to decide if it would be a good idea to replace most of our fairly convoluted React code with Turbo + Stimulus. I’ve chosen a fairly simple view for my initial take on such a rewrite.
The view consists of
- a table of data
- table headers that can be clicked to sort on that column
- pagination links
- pagination info (e.g. “showing row 1-10 of 180”)
- a select element for row count selection (i.e. display 10/20/50 rows)
- an input field for filtering/searching the table
The pagination and sorting links were easy enough to get working using Turbo Drive. It also worked pretty well for selecting a row count, for which I went with a Stimulus controller which submitted a form when the value changed and let Turbo handle the submission. I did however get stuck on implementing the search field.
- The search field should automatically refresh the results as the user types their query (though with a slight debounce)
- The field must not lose focus while new search results are loaded!
- The current page should be reset to 1, to avoid ending up at a non-existent page as the number of search results decreases
- The updated page parameter should be pushed to the URL
- I don’t have a strong opinion on whether the search query itself should be pushed to (or replace) the browser location though.
- My ideal solution would probably only use Turbo Drive and Turbo Frames and as little Stimulus as possible, simply to keep the complexity to a minimum, but perhaps that’s unreasonable
I’ve been struggling a lot with this seemingly simple task. The main problem I’ve had using Turbo is to ensure focus is kept in the search field even as the form is submitted and new search results are rendered, and that the page parameter is pushed to the URL so that a page reload does not change the page number that is displayed. A few of my failed work-arounds:
data-turbo-permanentto persist the search field, but it seems the element is cloned and then added, which means input focus is lost
- wrap the table and pagination in a Turbo Frame and let the search bar render only that, however that means the reset page number is not pushed to the URL
- do the above, but also add a Stimulus controller to the turbo-frame element that observes changes to the src attribute, but it seems it’s not possible to use a MutationObserver on the custom element
- find a way to hook into the fetch performed by Turbo to dynamically add the search query to the URL before it’s sent, but it seems the URL cannot be changed in e.g. the pre-fetch event
- use a custom renderer (i.e. morphdom) to ensure the search field is kept as is, but no luck (though there’s a PR that could potentially make this viable: Pausable rendering by domchristie · Pull Request #28 · hotwired/turbo · GitHub)
Now, I do have another implementation which completely skips Turbo and basically implements a similar approach using only Stimulus in about 80 lines spread over three controllers, but it feels (not very scientific, I know) like Turbo could provide a cleaner solution for this scenario.
Does anyone have some pointers or even solutions for this? I hope I’ve managed to explain the problem, but feel free to ask follow-up questions if something is not clear!