This is a shorter chapter. I’m going to show you how to communicate errors and success messages from your controllers to your views. We’re going to look at displaying custom messages to the user using a flashbox, and then showing form errors when an invalid form submission is made.

Flashboxes

We’re going to use some bootstrap-themed flash boxes to achieve this. We’re going to put them right in our base template app/views/layouts/application.html.erb. Add these lines:

### app/views/layouts/application.html.erb
...
<div class="container">
  <%= render 'layouts/navbar' %>
  
  <div class="row">
    <% flash.each do |key, value| %>
      <div class=" col-md-4 col-md-offset-4 alert alert-<%= key %>" style="margin-bottom: 20px"><%= value %></div>
    <% end %> 
  </div>
  
  <%= yield %>
</div>
...

Now, in the posts controller, add the following to posts#create:

### app/controllers/posts_controller.rb

...
  def create
    @post = current_user.posts.create(post_params)
    if params[:id]
      @post.receiver_id = params[:id].to_i
      @post.save
      flash[:success] = "Post successfully created!"
      redirect_to user_path(params[:id])
    else
      @post.save
      flash[:success] = "Post successfully created!"
      redirect_to newsfeed_url
    end
  end
...

Now if you create a post you should see the following:

Post successfully created

This is much better. You can add lines like flash[:success] = "Hello!" anywhere in your controllers to show these messages. If you’d like a warning box instead, which shows up in red, use flash[:danger] = "Oh no!".

Form Errors

We’re going to add a character limit to posts so that a malicious user can’t post an annoyingly long message to our newsfeed. When somebody makes a post that exceeds the character limit, we also need to give them feedback as to why that’s happened. First, we add the limit with a validation in our post model:

### app/models/post.rb

class Post < ActiveRecord::Base
  belongs_to :user
  belongs_to :receiver, class_name: 'User'
  default_scope { order(created_at: 'DESC') }
  
  validates :message, length: { maximum: 500 }
...

What we’re going to do is create a shared partial view that we include in every form. This will render any form submissions errors. Create this file in app/views/shared/_error_messages.html.erb:

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

<% if object.errors.any? %>
    <div class="alert alert-danger">
      <ul>
      <% object.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
<% end %>

The variable object will be passed to the partial as we render it (see the next codeblock). Render this partial at the top of our form in app/views/shared/_feed.html.erb:

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

<% if current_user %>
    <div class="panel panel-default">
      <div class="panel-body">
        <%= render 'shared/error_messages', object: @post %>
...

If you attempt to post a message longer than 500 characters in length, it won’t successfully post, but the error message also won’t be displayed. This is because in our posts#create method we handle both successful and unsuccessful post creations with a redirect. This redirect trashes all of our instance variables. @post is lost along with its error messages and replaced with a new one. We need to handle failures to save in our posts#create method with calls to render as opposed to redirect_to. Replace our existing create method with the following:

### app/controllers/posts_controller.rb

...
  def create
    @post = current_user.posts.create(post_params)
    if params[:id]
      @post.receiver_id = params[:id].to_i
      if @post.save
        flash[:success] = "Post successfully created!"
        redirect_to user_path(params[:id])
      else
        @user = User.find(params[:id])
        @posts = Post.all
        render 'users/show'
      end
    else
      if @post.save
        flash[:success] = "Post successfully created!"
        redirect_to newsfeed_url
      else
        @posts = Post.all
        render 'posts/index'
      end
    end
  end
...

And now it should work. Try to post an extra-long message anywhere, and you should get an error.

Message Too Long

Awesome.

Fixing A Bug

I’ve noticed that when you delete a post from somebody’s profile, it redirects you to the newsfeed. This isn’t great for user experience; I’d like users to stay on the same page when they delete a post. We can use redirect_to :back in our controller to send a user back to where they came from:

### app/controllers/posts_controller.rb

...
  def destroy
    @post = Post.find(params[:id])
    if current_user == @post.user
      @post.destroy
    end
    redirect_to :back
  end
...

Cool. Now we can move on to cleaning up our URLs in the next chapter.