The Flash

Exercises

Objective

In the previous exercise we gave users feedback when they tried to enter invalid data via forms. But sometimes it's nice to give them short status messages even when things go well. Indeed, it's often the case that web apps need to flash a message up on the page after a specific action takes place. We can easily do that with something Rails calls a flash (shocking, we know).

In this exercise our goal is to flash the following three messages after their respective actions:

  • "Movie successfully updated!"

  • "Movie successfully created!"

  • "Movie successfully deleted!"

We'll make quick work of this. We'll have it done in a flash! (OK, we'll stop now.) The first one takes a couple extra steps just to get everything set up, but then it's easy-peasy from there.

1. Flash on Update

First, when a movie has been successfully updated we want to give the user some confirmation that it happened. You know, something upbeat like "Movie successfully updated!".

  1. In the update action, use the flash object directly to assign the status message to the :notice key. Remember, the flash object acts just like a Ruby hash, so it needs a key (an arbitrary type, in this case :notice) and a value (the message you want displayed).

    Show Answer

    Hide Answer

    def update
      @movie = Movie.find(params[:id])
      if @movie.update(movie_params)
        flash[:notice] = "Movie successfully updated!"
        redirect_to @movie
      else
        render :edit
      end
    end
    
  2. Now, back in your browser, edit a movie and hit submit.

  3. Hey, where's our confirmation? Unfortunately, the flash message isn't displayed anywhere. :-(

    We set the flash message in the controller action, but now we need to actually display the flash message. That sounds like something a view template should do. But which view template? The update action ends up redirecting to the show action which then renders the show template. So displaying the flash in the show template would work, but it's a short-sighted solution.

    Ideally, we want to be able to set a flash message in any controller action and have it displayed at the top of the resulting page. For that reason, it's better to render the flash messages in the application-wide layout file. Say, that sounds familiar!

  4. In the application.html.erb layout file, render the flash message just inside the content div. Don't worry about stylin' it, just get something working.

    The flash object we used in the update action is also available in the layout (or any view). Use the flash object to display the message associated with the :notice key which we assigned in the update action.

    <div class="content">
      <%= flash[:notice] %>
    
      <%= yield %>
    </div>
    
  5. Now edit a movie again, and this time you should see the flash message displayed at the top of the movie's detail page. Reload the page and the flash should disappear.

    That makes sense. We don't want the flash message showing up every time we view a movie's detail page. We only want the message to appear after the user has updated the movie. The flash message (the value of the hash) is cleared after every request. It's like a flash in the pan! (Sorry, couldn't resist.)

  6. Want to add a little color and styling to the flash message? No problem. Click the answer for a version that displays the flash notice message in a styled paragraph if a notice message exists. It also displays any alert flash messages.

    Show Answer

    Hide Answer

    <% if flash[:notice] %>
      <div class="flash notice">
        <%= flash[:notice] %>
      </div>
    <% end %>
    
    <% if flash[:alert] %>
      <div class="flash alert">
        <%= flash[:alert] %>
      </div>
    <% end %>
    

    Now after editing a movie you'll see a greenish, centered flash message at the top. It's green because we have CSS rules for flashes. Green's not your favorite flash color? No worries. Just search for flash in the custom.scss file. You'll find specific rules for notice and alert messages. Feel free to change the colors!

  7. To keep everything in the layout file at basically the same conceptual level, let's put all the flash stuff in a partial, too.

    First, in the app/views/layouts directory, create a _flash.html.erb partial file. Then cut the flash code out of the application.html.erb layout file and paste it into the new _flash.html.erb partial.

    Then, back in the layout file, render the layouts/flash partial.

    Show Answer

    Hide Answer

    <div class="content">
      <%= render "layouts/flash" %>
    
      <%= yield %>
    </div>
    
  8. Finally, setting notice flashes is so common that Rails provides a shortcut. You can pass an option to redirect_to to automatically set the appropriate flash before the redirection happens.

    Change your update action to use the shortcut like this:

    def update
      @movie = Movie.find(params[:id])
      if @movie.update(movie_params)
        redirect_to @movie, notice: "Movie successfully updated!"
      else
        render :edit
      end
    end
    
  9. Give it a try in the browser. Very flashy indeed!

2. Flash on Create

Next we want to flash "Movie successfully created!" when a movie has been successfully created. Given what you've learned, you should be able to do this blindfolded!

  1. In the create action, assign the notice message to the flash object.

    Show Answer

    Hide Answer

    def create
      @movie = Movie.new(movie_params)
      if @movie.save
        redirect_to @movie, notice: "Movie successfully created!"
      else
        render :new
      end
    end
    
  2. In the browser, create a movie and the flash message should glow green at the top of the resulting page.

  3. We didn't have to change a view template for this to work. Why?

    Show Answer

    Hide Answer

    Flash messages are being rendered in the layout file, so any action can set a flash and it will get picked up by the layout.

3. Flash on Destroy

Try destroying a movie in the browser and you'll notice there's no visual feedback that it actually happened. Let's wrap up this exercise by fixing that, and adding a red twist...

  1. Change the destroy action to display a flash message when a movie is destroyed. This time make it an alert instead of a notice, since destroying stuff can be kinda alarming.

    Show Answer

    Hide Answer

    def destroy
      @movie = Movie.find(params[:id])
      @movie.destroy
      redirect_to movies_url, alert: "Movie successfully deleted!"
    end
    
  2. Remember that your _flash partial displays both notice and alert messages, so you don't need to make any changes to display alert messages.

  3. Double-check that it works in the browser by destroying a movie. You should see a reddish flash as a confirmation.

  4. If you want to re-populate the database with all the example movies, use:

    rails db:seeds:replant

    This task removes all the data from the database tables and "replants" the database with the seed data in db/seeds.rb.

Solution

The full solution for this exercise is in the flash directory of the code bundle.

Bonus Round

Flexible Flashes

This solution works good, but it has a limitation: the layout file currently only displays notice and alert flashes. You might want something a bit more flexible that can display arbitrary types of flash messages in a more generic way. It's a nice-to-have thing, so we went ahead and cooked up a solution for you.

Change your _flash.html.erb partial file to display flash messages like this:

<% flash.each do |type, message| %>
  <%= content_tag(:div, message, class: "flash #{type}") %>
<% end %>

Don't let this throw you. It just iterates through all the flash keys and values, and uses the content_tag helper to generate a styled div tag for each message. That way, whatever you put in the flash will get displayed with a little style. Notice that by using an iterator we don't need a conditional because the iterator only runs for the keys that are in the flash.

Make sure to remove the div tags we added earlier to render the notice and alert flashes separately. Otherwise you'll get duplicate flash messages!

Give it a whirl in the browser just to make sure the same green and red flashes show up!

Custom Flash Types

By default, Rails supports setting the :notice and :alert flash types when calling the redirect_to method. But sometimes you want to conveniently set a custom flash type when redirecting. For example, suppose you wanted to set a :danger flash type like so:

redirect_to movies_url, danger: "Danger, Will Robinson!"

To do that, you'll need to register the :danger flash type:

  1. Add the following line inside of the ApplicationController class, which is defined in the app/controllers/application_controller.rb file:

    class ApplicationController < ActionController::Base
      add_flash_types(:danger)
    end
    

    The ApplicationController is the base (parent) class that all other controllers inherit from (subclass), so anything you put in here applies to all controllers.

  2. As an example of how you might use your custom flash type, comment out the code currently in the destroy action in your MoviesController and instead just redirect with a danger message, like so:

    def destroy
      ...
      redirect_to movies_url, danger: "I'm sorry, Dave, I'm afraid I can't do that!"
    end
    
  3. Now try to delete a movie and you should see the (unstyled) danger message flashed at the top of the page. Before the redirect happened, Rails automatically assigned the message to flash[:danger]. Then after the redirect happened the message got displayed because your flexible _flash.html.erb partial iterates through all the flash keys and values. You saw that coming, right?

  4. Finally, add some style to danger messages. You'll need to define a CSS rule in custom.scss that matches the CSS classes flash and danger.

    Show Answer

    Hide Answer

    .flash {
      /* existing styles here */
    
      &.danger {
        background-color: #FFA715;
      }
    }
    
  5. Remember to revert your destroy action back to its original form before moving on!

Wrap Up

News flash: our app is now giving lots of good feedback! Flashes are kinda fun, but don't go overboard with them. It's typical to show flash messages to confirm the success of the create, update, and destroy actions as we've done in this exercise. Flashes are also useful for things like a "Welcome back!" message after a user has logged in and a "So long!" message when they log out. But try not to get too flashy, ok?

Now that we have the movie side of the house in good shape, it's time to shift gears and work on adding reviews for each movie. That's up next! But first, you deserve a break, and you knew we couldn't resist recommending this movie.

Dive Deeper

To learn more about the flash, refer to "The Flash" section in the Rails Guide: Action Controller Overview.

 

Congratulations! You're halfway through the course!

Thanks for checking out these free modules. We've be delighted to have you in the Studio for the rest of the course. Use coupon code "FINISHCOURSE" to save 20%!