Why is the Controller "target" attribute a space-delimited list?

I’ve noticed developers coming to hotwire who spent some time building SPA’s seem to feel a phobia for sending requests to the backend (myself included). As a result, they write lots of javascript code to avoid sending requests to the backend.

One of the main motivations for avoiding sending requests to the backend is to save server resources in order to help make the application run faster for all users. This is the SPA way. I asked this question early on when I first began experimenting with hotwire in 2020. The irony is, after more than 2 years building applications with hotwire I can now say my applications run much faster with hotwire than they ever did with an SPA framework.

We try and suppress the urge to limit requests to the backend. We step back and ask ourselves first: how would I build this without javascript? This usually reveals a better pattern for building a solution.

This last week we made the mistake of building a new feature with a hotwire first approach and the results turned out overly complicated. We then stepped back and asked ourselves: how would we build it without any javascript? A better and less complicated solution revealed itself with this one question. We then built it to work without javascript, and then enhanced it with javascript.

Using the last solution as an example, how would you build this without javascript?

  1. The genre form would contain an action targeting the same page and a submit button. After the user selects the option they click the submit button.
  2. The page then reloads with the genre option still selected and the category form with a populated select, an action on the form and a submit button.

Now that it’s working without javascript I can “sprinkle” a little javascript to enhance the experience to make it easier for the user (they don’t have to click “submit” to get the second form if they have javascript).

  1. Use stimulus to submit itself on select change.
  2. Add a css class to hide the submit buttons by default, but unhide it if they do not have javascript enabled (class="hidden no-js:inline"). Note: In every project I have a javascript snippet which flags the DOM for pages which do (or do not) have javascript, so this css class just works without any JS.

At this point with turbo running you might be “good” and require no additional enhancements. To determine if more work is required, some questions you might ask yourself:

  1. Does the user experience a major/jarring scroll position change after submit?
  2. Do we want to update the page without updating the browser history?
  3. Does the backend or frontend HTML view contain too much logic and I want to separate the concerns?

If answering “yes” to at least of the above questions, I then reach for a turbo-frame or turbo-stream element to enhance the user experience or simplify the logic.

Either way, the amount of javascript to make it work well for the user is kept to a minimum and the experience for the user is the same or better than adding more javascript.

2 Likes