As I read the excellent post abouthow to implement lazy loaded tooltips with Hotwire by Sean P. Doyle and Steve Polito, I couldn’t help but think that this is actually a very distinct example of how hard it is to draw the line between anall-in REST approach (i.e., controllers and routes forallthethings) and a more nuanced take on things that tries tobalance REST and RPC ways of implementing Reactive Rails.
So I decided to write up a quick reply usingfuturism, the lazy loading solution of the CableReady ecosystem, just to serve as an example of an alternative which maybe aligns better with existing legacy codebases (because it relies on plain Rails partials - that might even already exist in your application!).
Let’s get started!
1. Install futurism
First, let’s install futurism. Sean’s repository uses importmaps, so we’ll have to do a bit of manual installation inapp/javascript/controllers/index.js
as long as the Futurism installer hasn’t been fully ported.
$ bundle add futurism -v 1.2.0.pre9$ bin/importmap pin @stimulus_reflex/futurism@1.2.0-pre9// app/javascript/controllers/index.jsimport { Application } from "@hotwired/stimulus";// NEW: import cable from turbo-rails and futurismimport { cable } from "@hotwired/turbo-rails";import * as Futurism from "@stimulus_reflex/futurism";const application = Application.start();// Configure Stimulus development experienceapplication.debug = false;window.Stimulus = application;export { application };// NEW: initialize futurismconst consumer = await cable.getConsumer();Futurism.initializeElements();Futurism.createSubscription(consumer);
Essentially, this initializes Futurism with the default ActionCable consumer, and intializes the custom elements used under the hood.
2. Implement Lazy Loading
Sean and Steve use a Turbo frame in the show view, as well as in theuser
partial to load the tooltip. To quote the original article/source code:
<!-- app/views/tooltips/show.html.erb --><turbo-frame> <div> <div><%= render partial: "users/user", object: @user, formats: :svg %> <strong>Name:</strong><%= link_to @user.name, @user, class: "text-white" %> </div> <div></div> </div></turbo-frame><!-- app/views/users/_user.html.erb --><turbo-frame role="tooltip" src="<%= user_tooltip_path(user, turbo_frame: dom_id(user, :tooltip)) %>">
Now, among other things, this leads to a bit of confusion for the first-time reader regarding the addressing of the frame: To have Turbo swap out the correct frame, we need to pass the identifier as a parameter down to theTooltipsController
usinguser_tooltip_path(user, turbo_frame: dom_id(user, :tooltip))
.
Using futurism, I’m going to take a slightly different approach. First I’m going to move the contents from the tooltipsshow.html.erb
to a_tooltip.html.erb
partial (note that the content can stay exactly the same, we’re just dumping the surrounding<turbo-frame>
):
<!-- app/views/tooltips/_tooltip.html.erb --><div> <div><%= render partial: "users/user", object: user, formats: :svg %> <strong>Name:</strong><%= link_to user.name, user, class: "text-white" %> </div> <div></div></div>
In_user.html.erb
, I exchange the Turbo frame for a futurize call and restructure the markup a bit (to make the peer-hover work, I wrapped it in an enclosing<span>
).
All you have to know at this moment is that it obeys the API ofrender partial:
and you can pass a placeholder to the block (which I omitted here):
<!-- app/views/users/_user.html.erb --><div> <p> <strong>name:</strong><%= user.name %> </p> <p><%= link_to "show this user", user, class: "peer", aria: { describedby: dom_id(user, :tooltip) } %> <span><%= futurize partial: "tooltips/tooltip", locals: {user: user}, extends: :div do %><% end %> </span> </p></div>
The result is an equivalent functionality, albeit with a full RESTful route including all the boilerplate less. This time, we’re using a<futurism-element>
to communicate with ActionCable and CableReady does the heavy lifting in the background. Observe:
3. Conclusion
I hope you enjoyed this comparison. It’s important to me to spell out that there’s no right or wrong here. I wrote futurism prior to Turbo frames even being a thing, and find myself using the latter in a majority of cases these days. But sometimes, a less intrusive approach is preferable, especially when dealing with legacy codebases, or really small DOM fragments, where a full-fledged ActionDispatch route would be an overdo.
The interested reader might want to take a look at thefuturism README for further information on the library.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse