Typically the way to implement behavior like this in Rails is to create a form with the
Using SJRs to add sprinkles of interactivity has a low barrier to entry, and is often a great solution. But once the complexity of the action increases, SJRs quickly become bloated with conditional logic. For instance, you might want to perform different updates to the frontend depending on the page that you are creating the comment from, or maybe you have multiple comment areas on a single page that need to be updated. All of a sudden your SJR is not two or three lines, but a mess of complex code handling all of these different scenarios. When things get this complex we need a framework to organize the frontend logic—enter Stimulus.
For those that know a bit of Stimulus, or have read the Stimulus Handbook, the obvious way to do this is to render the textarea and a button, attach a click event action to the submit button, and finally initiate an ajax call from within your Stimulus controller using jQuery or
fetchto grab the new comment html to be appended. This seems reasonable, but it also means you lose all of the awesome ujs goodness that makes ajax so easy in Rails. Wouldn’t it be nice to still use a remote form and offload the ajax call to rails-ujs like we normally do?
Fortunately, leveraging rails-ujs together with Stimulus is actually fairly straightforward! The two keys to it are (i) we need to specify the remote form to have html datatype, and (ii) specify a Stimulus action for the
ajax:success event. Time for some code:
Our comment list is fairly straightforward. We add the Stimulus
data-controller attribute to the container, as well as a
data-target to the list itself. We use
form_with which defaults to
remote: true. In our
data attributes on the form we specify
type: "html" and
action: "ajax:success->comment-list#onPostSuccess. This connects the form submission ajax request performed by rails-ujs to our Stimulus controller action.
In our Rails controller we just render the newly created comment, returning html instead of an SJR:
Our Stimulus controller handles updating the DOM and clearing the form:
Now all of our UI logic is handled by Stimulus, while our backend just provides the rendered html. We can easily add multiple comment areas in a single page without adding additional
A reader asked how you would handle errors in this setup. You would use the same exact approach with an additional Stimulus action
ajax:error->comment-list#onPostError. Your Rails controller would return the desired errors to be rendered as html, and your Stimulus controller would render those errors. A complete gist detailing this is shown below: