DEV Community

Cover image for Flash messages with Hotwire
Anders Klenke
Anders Klenke

Posted on

Flash messages with Hotwire

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:
1

A simple form with an alert on top when submitted πŸŽ‰
2

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 %>
Enter fullscreen mode Exit fullscreen mode

app/views/layouts/_flash.html.erb

<% flash.each do |type, message| %>
  <% if type == "notice" %>
    <%= message %>
  <% end %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

In the code above, in the case that the update action is succesful:

  1. Create a flash message to be used in the same request with the message "Place was updated".
  2. 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)

Collapse
 
bdavidxyz profile image
David Boureau

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.

Collapse
 
ternggio profile image
Lee Terng Gio

I'm using datepicker in the form, and it's broken after the form is replaced. How do I solve this?

Collapse
 
ur5us profile image
Juri Hahn

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.