Kerrison Garcia
Kerrison Garcia

Reputation: 13

Rendering form errors in the current view in Rails

I have an MainPagesController index page that is rendering the 'new' page from QuotesController that has a form. How do I render the MainPagesController index page with the errors of the form?

MainPages/index

<h1>Welcome to Book Quotes</h1>
<p>
  Post your favourite quotes from your favourite books
  <%= render 'quotes/new' %>
</p>
<%= render 'quotes/all_quotes' %>

Quotes/new

<h1>Add a quote</h1>
<%= render 'quotes/form' %>

Quotes/_form

<%= form_for @quote do |f| %>
  <% if @quote.errors.any? %>
    <ul>
    <% @quote.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  <% end %>
  <p>
    <%= f.label :passage %><br>
    <%= f.text_field :passage %>
  </p>
  <p>
    <%= f.label :book_title %><br>
    <%= f.text_field :book_title %>
  </p>
  <p>
    <%= f.label :book_author %><br>
    <%= f.text_field :book_author %>
  </p>
  <p>
    <%= f.submit %>
  </p>
<% end %>

QuotesController

def create
  @quote = Quote.new(quote_params)
  if @quote.save
    redirect_to root_url
  else
    render #not sure what goes here
  end
end

Upvotes: 0

Views: 4214

Answers (1)

Robert Nubel
Robert Nubel

Reputation: 7532

Since the form that you're dealing with is a nested form, the standard advice of render :new won't help you here. Instead, you could redirect the user back to the index page, passing the errors via the flash, and update your view to handle displaying those errors.

(Just a thought: it might be worth looking into making this action powered by AJAX. The user experience might be nicer, and it simplifies your code design.)

Anyway, in your QuotesController, the #create action needs to note the errors and pass them along as it redirects the user back to where they came from:

def create
  @quote = Quote.new(quote_params)
  if @quote.save
    redirect_to root_url
  else
    flash[:quote_errors] = @quote.errors.full_messages
    redirect_to :back # or main_pages_path
  end
end

Then, your Quotes/_form view needs to handle those errors:

<%= form_for @quote do |f| %>
  <% if flash[:quote_errors] %>
    <ul>
    <% flash[:quote_errors].each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  <% end %>
  # ...

Now, this is a bit ugly. You might be wondering -- couldn't we just pass the @quote object back via the flash, so the view doesn't have to change? But while that's technically possible, serializing objects into session is a dangerous path to take. I'd suggest avoiding it.

One other option would be to make the quote submission an action not on QuotesController, but on your MainPages controller. E.g.,

class MainPagesController < ApplicationController
  def index
    # ...
  end

  def create_quote
    @quote = Quote.new(quote_params) # need to move quote_params in, too
    if @quote.save
      redirect_to root_url
    else      
      render :index
    end
  end
  # ...

This allows the @quote instance variable to be accessible from your form, so the error handling as-is will work just fine. It's not very RESTful, but then again, neither are most front-end website flows.

Upvotes: 3

Related Questions