Last week Basecamp released their NEW MAGIC aka Hotwire and I finally had the time to have a look at it.
The first thing that come to mind was forms and flash messages. There's already a ton of videos showing how to use Turboframes with forms so I don't want to go into too much detail on that topic. But what I would like to show you is how you can add flash messages that pops up like an alert.
A simple form with an alert on top when submitted π
The first thing I do is to wrap my flash message partial in a turbo_frame_tag
. By doing this, we can replace this part of the page on its own later on.
app/views/layouts/application.html
<%= turbo_frame_tag "flash" do %>
<%= render "flash" %>
<% end %>
app/views/layouts/_flash.html.erb
<% flash.each do |type, message| %>
<% if type == "notice" %>
<%= message %>
<% end %>
<% end %>
I also added a turbo_frame_tag around my form partial
app/views/places/edit.html.erb
<%= turbo_frame_tag "admin_place_form" do %>
<%= render 'form', place: @place %>
<% end %>
My form is just a regular form partial using a form_with
helper and a dom_id
helper. The dom_id
sets a unique ID that can be replaced.
app/views/places/_form.html.erb
<%= form_with model: place, id: dom_id(place) do |form| %>
<%= form.text_field :name %>
<%= form.submit %>
<% end %>
And now the controller code. There's probably a better way to do this. If you know how, please let me know in the comments. I'm just learning this π But here it is:
app/controllers/place_controller.rb
class PlacesController < ApplicationController
def update
@place = Place.find(params[:id])
if @place.update(place_params)
respond_to do |format|
flash.now[:notice] = "Place was updated!"
format.turbo_stream { render turbo_stream: turbo_stream.replace("flash", partial: "layouts/flash", locals: { flash: flash }) }
format.html { redirect_to edit_admin_place_path(@place.id) }
end
else
render turbo_stream: turbo_stream.replace(@place, partial: "places/form", locals: { place: @place })
end
end
end
In the code above, in the case that the update
action is succesful:
- Create a flash message to be used in the same request with the message "Place was updated".
- Use turbo_stream to replace the dom id
flash
with the partial containing the flash message.
In case of an unsuccessful update, eg. validation failed, I simply replace the form partial with some validation errors.
That's it π
Top comments (3)
My humble opinion : your solution is very ok, and stick to the Rails Way. However I find it very complicated. Simply display a message to the user should be completely straightforward. There's already a flash message in Rails for this ! Thus, the flash message should be displayed, no matter the technical stack behind it. Note it is already the case for the error scenario. Turbo is able to render an error flash message. But not a successful one ! I understand that "TurboDrive" is meant to navigate, and "TurboFrame" for inline edition, but honestly it shouldn't be so painful to simply render a message about the last action.
I'm using datepicker in the form, and it's broken after the form is replaced. How do I solve this?
How to solve this largely depends on how the datepicker is initialized. Without knowing what is used itβs impossible to tell what/why itβs not working for you. In general though, jQuery based approaches that initialize widgets on page load must be re-initialized. Moreover, one might want to take extra care to de-register (e.g. unbind events) such components before removing them from the DOM tree. Anyway, as mentioned in my introductory sentence what and how to do it largely depends on the datepicker library. Iβd recommend using a library that relies on StimulusJS in this case as that should just work.