form_for error messages in Ruby on Rails
Same as Rails 3 -- see f.error_messages in Rails 3.0 or http://railscasts.com/episodes/211-validations-in-rails-3 for many different possibilities.
My personal preference is to use simple_form and have it put the error next to the input.
Rails 7 signup form doesn't show error messages
If you look at the logs you can see that Rails is getting an AJAX request in the form of a turbo stream:
Processing by UsersController#create as TURBO_STREAM
Where it should read:
Processing by UsersController#create as HTML
To disable turbo you want need to set a data-turbo="false"
attribute on the form:
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: @user, data: { turbo: false }) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
The local: false
option only works with the old Rails UJS javascript library which was the default prior to Rails 7. You can also disable Turbo by default with:
import { Turbo } from "@hotwired/turbo-rails"
Turbo.session.drive = false
See:
https://turbo.hotwired.dev/handbook/drive#disabling-turbo-drive-on-specific-links-or-forms
Showing form error messages
Errors
The problem isn't anything to do with the way you're rendering the form (render
or redirect
) - it's to do with the way you're handling your ActiveRecord
object.
When you use form_for
, Rails will append any errors into the @active_record_object.errors
method. This will allow you to call the following:
form_for error messages in Ruby on Rails
<%= form_for @object do |f| %>
<% @location.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
<% end %>
This only works if you correctly create your ActiveRecord
object, which you seem to do
--
Nested
#config/routes.rb
resources :topics do
resources :posts, path: "", path_names: {new: ""}, except: [:index] #-> domain.com/topics/1
end
You'll be much better using the following setup for a nested
route:
<%= form_for [@topic, @post] do |f| %>
...
<% end %>
This allows you to create a form which will route to the topics_posts_path
, which is basically what you need. The controller will then balance that by using the following:
#app/controllers/topics_controller.rb
Class TopicsController < ApplicationController
def new
@topic = Topic.find params[:topic_id]
@post = Post.new
end
def create
@topic = Topic.find params[:topic_id]
@post = Post.new post_params
end
private
def post_params
params.require(:post).permit(:attributes)
end
end
Why does simple form not show validation error messages in Rails?
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
Form Error Messages Not Generating in Rails App
As I said, you need to change
redirect_to '/signup'
to
render 'new'
From the Guides
The
render
method is used so that the@user
object is passed back
to thenew
template when it is rendered. This rendering is done within
the same request as the form submission, whereas theredirect_to
will
tell the browser to issue another request.
That said, so as the redirect_to
issues a new request to the browser, the values of @user
is lost, in other words @user
is a new instance that is instantiated again. That is why <% if @user.errors.any? %>
always returns false
as if there are no errors in @user
.
Related Topics
Ruby: Combine Date and Time Objects into a Datetime
Rails 3.1 Absolute Url to an Image
Failing Installing Pg Gem, "Mkmf.Rb Can't Find Header Files for Ruby" (MAC Osx 10.6.5)
The Compiler Failed to Generate an Executable File. (Runtimeerror)
Handling Exceptions Raised in a Ruby Thread
Trying to Install Ruby-Filemagic on Snow Leopard Using Brew Rather Than Ports
How to Fire Raw Mongodb Queries Directly in Ruby
Setter Method (Assignment) with Multiple Arguments
Render Three Different Partials Depending on Button Clicked
How to Use Params with Slashes with Sinatra
How to Match Full Words and Not Substrings in Ruby
Selenium-Webdriver Ruby --> How to Wait for Images to Be Fully Loaded After Click
How to Install Ruby 2.1.4 on Ubuntu 14.04
Ruby: What Is the Easiest Way to Remove the First Element from an Array