Building App Wide Flashes in Rails

Make It Flashy

We're not making any fashion statements in this post but we will be setting up some flash messaging for an application using Rails' ActionDispatch::Flash class. Technically speaking, a Flash in Rails acts like Hash / Session object where values can be stored. However, it is unique in that values stored there will only be available in the next request and are discarded after each action. This makes them valuable for presenting warnings or errors to users on form submission.

From a design perspective, a "flash notice" or "flash" is an implementation of the principle of feedback for users. It provides a temporary notification or alert when using with the software system. Feedback, in essence, informs a user of the consequence or rwsult of the action they have taken on the system (positive or negative). It may seem like a minor point but remember that adding small design elements like this is significant to the process of building human centered software.

This Is A Warning

Now that we know what a flash is, let's add them into our ApplicationController. To do this we will start with a new endpoint to our ApplicationController to display a temporary warning for testing. Open your config/routes.rb and add a new route to the endpoint /warning as shown below:

# config/routes.rb

Rails.application.routes.draw do
  root to: "pages#welcome"
  get "/warning", to: "application#warning" 
end

We map our /warning endpoint onto a new action in the ApplicationController aptly named warning which is defined below. It's a small amount of code but it sets up a big result so let's take it step by step.

# app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  add_flash_types :success, :warning, :danger, :info ❶ 

  def warning ❷
    flash[:warning] = "You have been warned!"❸
    redirect_to root_path ❹
  end
end

The big picture goal here is to create a flexible alerting system that can be used in any controller that descends from ApplicationController in order to display correctly styled messages to the user. To accomplish this we make use of Bootstrap's sentiment colours as "flash types" to set errors, warnings, and prompt values to display. Bootstrap provides a selection of colours[^fn16] utilities (also called sentiments) to communicate different meanings. Some of these colours include warnings (yellow), danger (red), information (light blue), success (green) and more. To be able to use these new types we need to expand Rails' flash type selection to include our desired keys. We do this by calling the add_flash_types ❶ method in the ApplicationController and passing it the keys we'd like from Bootstrap. Recall that the Rails Flash duck types like a Hash so you can view these types as adding new potential keys to the Hash. Once we've added the new types we go on to define our test action warning ❷ whose method body calls the flash method to set the value of the warning key ❸ to a default message and redirect finally to the root path ❹.

Creating the Flash Template

We have set up the basic plumbing for our alerting but you will notice that nothing is displayed if you visit the /warning route right now. This is because we still need to construct the view to display these messages. We can do that by creating a new partial template called shared/_flash.html.erb and rendering it in our layouts/application.html.erb.

Using Bootstraps prebuilt Alert component, we can construct the generic partial template for alerting below using some string interpolation awesomeness:

# app/views/shared/_flash.html.erb

<div class="flash-container">
 <% !flash.empty? %> 
  <div class="container py-2">
    <% flash.each do |f_name, f_msg| %> ❶
      <%= content_tag :div, class: "alert alert-#{f_name} ❷ alert-dismissible fade show" do %>
        <%= f_msg %> ❸
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
        <span aria-hidden="true">&times;</span> 
       </button>
     <% end %>
    <%end%>
  </div>
 <% end %>
</div>

Recall that the Flash object is just a special part of the session and so is available in the context of the application layout. Because of this, we can access the flash object inside an embedded Ruby block and iterate through each message ❶ to render it to the page. In our case, we first check if there are any messages and if so render each message as a new div tag using the content_tag method, style it using Boostrap classes and set the inner HTML ❸ . The trick that makes all this work together is the use of string interpolation ❷ to dynamically set the class of the alert that gives it the correct style and colouring using the name of the flash message, which as you will recall if one of the flash types we defined previously and use in our controllers.
The final step to get our alerts to display is to render the partial inside our layout/application.html.erb:

# app/views/layouts/application.html.erb

  <body class="d-flex flex-column h-100">
    <%= render 'shared/nav'%>
    <main role="main" class="flex-shrink-0">
    <%= render 'shared/flash'%>
     <div class='content' >
      <%= yield %>
     </div>
    </main>
    <%= render 'shared/footer'%>
  </body>

Now, if navigate to the new /warning route you should be redirected to the root page and see our lovely new flash alert! With all this wired up, now whenever we call flash[:warning] in the controller we know will get a corresponding yellow alert containing our message displayed to the page. The same goes with flash[:danger] but instead of yellow it will be red, flash[:success] (green), flash[:info] (light blue), etc.