Search code examples
ruby-on-railsrubysimple-form

Why does simple form not show validation error messages in Rails?


The :new view when redirected if validations are not matched and where I'd like to see the error messages:

<%= simple_form_for ([ @recipe, @recipe.comments.build]), class:"comment-form" do |f| %>
<%= f.error_notification %>
<%= f.object.errors.full_messages.join(", ") if f.object.errors.any? %>
<%= f.input :name, label: false, placeholder: "Your name", input_html: { value: @comment.name } %>
<%= f.input :comment, label: false, placeholder: "Tell us about your experience", input_html: { value: @comment.comment } %>
<%= f.submit "Submit", class: "btn-comment-submit" %>
<% end %>

This is my controller:

  def new
    @recipe = Recipe.find(params[:recipe_id])
    @comment = Comment.new
    @comment = @recipe.comments.build
  end

  def create
    @comment = Comment.new(comment_params)
    @recipe = Recipe.find(params[:recipe_id])
    @comment.recipe = @recipe
    if @comment.save
      redirect_to recipe_path(@recipe)
    else
      render :new
    end
  end

Solution

  • You're not binding the @comment instance you have created in your controller to the form. Instead @recipe.comments.build always creates a new instance of Comment.

    You can set the model with a conditional:

    <%= simple_form_for([@recipe, @comment || @recipe.comments.build]) do |form| %> 
      <%= f.error_notification %>
      <%= f.object.errors.full_messages.join(", ") if f.object.errors.any? %>
      <%= f.input :name, label: false, placeholder: "Your name" %>
      <%= f.input :comment, label: false, placeholder: "Tell us about your experience" %>
      <%= f.submit "Submit", class: "btn-comment-submit" %>
    <% end %>
    

    Note that you don't need to set the values for the inputs. The form builder will do that for you. Thats kind of the whole point of it.

    Or you can preferably ensure that you're setting @comment in the controller to keep the view as simple as possible:

    class RecipiesController < ApplicationController
      before_action :set_recipe
      # ...
      def show
        @comment = @recipe.comments.new
      end
    
      # ...
    end
    
    <%= simple_form_for([@recipe, @comment]) do |form| %>
       # ...
    <% end %>
    

    And you can clean up your create action and just create the comment off the recipe:

    def create
      @recipe = Recipe.find(params[:recipe_id])
      @comment = @recipe.comments.new(comment_params)
      if @comment.save
        redirect_to @recipe
      else
        render :new
      end
    end