Rails 6.1 - Hotwire with Flash Messages
A simple Rails App that works off one page using flash messages
Overview
Now that you have the basics of using Hotwire in Rails Using Hotwire in Rails - its interesting to try using it in other contexts, inparticular modals are very useful for inputs in Single Page Apps. So in this Blog we will make the new input form a modal and leave the edit as an in-place form.
Basic Setup
Start with the code at the end of: Using Hotwire in Rails
Flash Messages in Partial
Remember, turbo_streams requires a dom_id and a partial in order to know where to send / update the HTML it generates – so let’s prepare application.html.erb
so that flash messages use partials.
# app/views/layouts/application.html.erb
<body>
<%= render "shared/notice", notice: notice %>
<%= yield %>
</body>
and of course we need a partials for notices now (we will keep it very simple):
# app/views/shared/_notice.html.erb
<p id="notice"><%= notice %></p>
now we will create a turbo template to handle the flash on create:
# app/views/tweets/create.turbo_stream.erb
<%# to send a message to the notice partial %>
<!-- action dom_id partial with dom_id data to send in the notice -->
<%= turbo_stream.append "notice", partial: "shared/notice", locals: {notice: "Tweet created."} %>
In order for the controller and turbo_stream to handle this non-standard action we need to update the create method in the controller with the instructions format.turbo_stream
on a successful create:
# app/controllers/tweets_controller.rb
def create
@tweet = Tweet.new(tweet_params)
respond_to do |format|
if @tweet.save
format.turbo_stream # enables flash message on create - via the create template
format.html { redirect_to tweets_url, notice: "Tweet was successfully created." }
format.json { render :show, status: :created, location: @tweet }
else
format.turbo_stream { # route turbo validation errors
render turbo_stream: turbo_stream.replace(
@tweet, partial: "tweets/form",
locals: { tweet: @tweet}) }
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @tweet.errors, status: :unprocessable_entity }
end
end
end
Now when we test everything works great, except our form no longer clears. We can fix that by adding a second action to the create template (we will send a Tweet.new - there are other approaches too - covered in Hotwire and StimulusJS)
# app/views/tweets/create.turbo_stream.erb
<%# clear form on create - without using JavaScript - by replacing the old Tweet info with Tweet.new %>
<%= turbo_stream.replace "tweet-form", partial: "tweets/form", locals: { tweet: Tweet.new } %>
<%# to send a message to the notice partial %>
<%= turbo_stream.append "notice", partial: "shared/notice", locals: {notice: "Tweet was successfully created."} %>
Refactor
You might have noticed, that we have moved most of our turbo_steam template to the template file, but not the replace for validation errors – since we already have a replace
command in our template - we will need to leave our specific instructions in the errors as is – until we clear the form with JS.
NOTE: now that we are consolidating our template info it might be tempting to add the following:
<!-- to prepend on create - disabled to avoid double vision when broadcasting -->
<%#% stream_action dom_id_target, render_partial, send_local_variables %>
<%= turbo_stream.prepend "tweets", partial: "tweets/tweet", locals: { tweet: @tweet } %>
but don’t add the default happy path instructions to the template when a model already has a broadcast after hook - if you add this instruction the person creating a new tweet will see two!
Flash after we update
This is now very straight forward we simply add format.turbo_stream
to our save and create an update.turbo_stream.erb
template
# app/views/tweets/update.turbo_stream.erb
<%# to send a message to the notice partial %>
<%= turbo_stream.append "notice", partial: "shared/notice", locals: {notice: "Tweet was successfully created."} %>
And now we can tell the controller to use that:
# app/controllers/tweets_controller.rb
def update
respond_to do |format|
if @tweet.update(tweet_params)
format.turbo_stream
format.html { redirect_to @tweet, notice: "Tweet was successfully updated." }
format.json { render :show, status: :ok, location: @tweet }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @tweet.errors, status: :unprocessable_entity }
end
end
end
We don’t have to clear the form on update since the edit
template is replaced with the show
template already. So we are done.
Resources
The repo where you can find this code in the branch: https://github.com/btihen/ruby_kafi_hotwire_tweets/commits/hotwire_flash_messages