We’re using Rails 7 w/ Turbo + Stimulus. Like others posting in this thread, we’re finding view components (from GH) to be essential to keeping highly cohesive things together. It produces a much more readable and maintainable system that oodles of partials throughout. Using Evil Martians gem to further enhance the sidecar experience (locating the view component rb alongside the slim/erb in the component directory) is also nice.
Re: the original question of “how are we handling complex JS components”?.. we’re trying to align with the vision of unobtrusive JS and sprinkling abstract/utility stimulus controllers as much as possible. I’m currently working on a fairly hi-fi page. If I go with that strategy, I’ll end up with 7 or 8 different stimulus controllers controlling things all over the page. I’m hestitating… looking for a cleaner design. I don’t really want to eject out of Hotwire and deliver this with a React component, though, b/c the page is composed of building blocks used elsewhere in the app (really don’t want to duplicate those components into 2 ecosystems).
TL;DR Still looking for a great solution. Likely will make a stimulus controller for the page that brings together what 3 or 4 separate stimulus controllers might have done
Yes we are facing similar issues here. I believe it’s just a matter of time until we have to use vuejs or another client-side library to handle complex pages.
A few updates below. For context, the page we’re working on is a search form, allowing the user to cherry pick result items to include in their project/whatever.
We had several stimulus controllers managing the search form itself:
- to verify that required fields were populated before enabling the submit button
- to verify that invalid fields were changed before enabling the submit button
- to perform a logic operation (AND) on the results of the 2 said controllers (required fields and valid fields)
- to clear the error state of an invalid field in response to a change event on that field
We ended up consolidating much of this into a single “FormController”.
Also, we noticed that between these controllers and others, there was a pattern emerging:
- monitor a group of input fields (eg input & change events)
- form a conclusion about the state of something else based on the state of those inputs
- affect that something else
For example (a controller ensuring that required fields are populated before being allowed to submit a form):
- monitor inputs with the
- detect a blank value of any of those elements
- disable the submit button if any required inputs were blank; enable it otherwise.
Or this one (a badge displaying the count of active filters in a section)
- monitor inputs under a collapsing section header (use targets for this one)
- count the number of non-blank elements
- set the innerText of an outputTarget to equal the count
We extracted a map-reduce style mix-in that can be composed into stimulus controllers, and configured with callback functions for:
- input (return elements to monitor)
- map (transform element into information of interest)
- reduce (combine the array of values of interest into a final answer)
- output (change the state of other elements based on the final answer)
Here’s an example of it being used to implement the required fields controller logic:
input: this.requiredFields, // searches the form.elements for fields having `required` class
map: elementValue, // get the value of each element
reduce: this.requiredConditionsSatisfied, // yield true if all values are present?
output: requiredConditionsSatisfied => setButtonEnabled(this.outputTarget, requiredConditionsSatisfied)
This ended up reducing a lot of our boiler-plate.
Along with carefully choosing a more course-grained stimulus controller (a single form controller instead of 3 or 4 sewn together), the team is much happier with the maintenance job remaining.
The combination of Hotwire and the View Components gem has been a great combination for us. Makes creating simple reusable components easy to build/maintain.