When I send wrong email, validation can't pass but error messages in views don't be displayed :(
I have in models:
validate :recipient_not_have_invitation, :notice => "That user have already invitation"
def recipient_not_have_invitation
errors.add :notice, 'That user have already invitation' if InvitationToGroup.find_by_recipient_email_and_group_id(recipient_email, group_id)
end
in controller:
(...)
if #invitation_to_group.save
Mailer.invitation_to_group(#invitation_to_group).deliver
redirect_to root_url, :notice => "Successfully send invitation to user #{#invitation_to_group.recipient_email}"
else
redirect_to new_invitation_to_group_path(:group_id => #invitation_to_group.group_id)
end
In views (invitation_to_groups/new.html.erb)
<h2>New Invitation to group </h2>
<% if notice %>
<p id="notice"><%= notice %></p>
<% end %>
<%= form_for #invitation_to_group do |f| %>
<% if #invitation_to_group.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#invitation_to_group.errors.count, "error") %> prohibited this user from being invitation:</h2>
<ul>
<% #invitation_to_group.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
(...)
The problem is that you are using redirect_to after else in your controller. So you are going to new action and in this action you have InvitationToGroup.new. So you build new object without errors ;) You need to use render method instesd.
PS. you should really consider using "formtastic". It will imporve your code in views. Please watch this two railscasts: http://railscasts.com/episodes/184-formtastic-part-1 and http://railscasts.com/episodes/185-formtastic-part-2
Related
I have something like
<div class="userInput">
<%= form_for :scribble do |f| %>
<%= f.text_area :scribble, cols: 65, rows: 4,:maxlength => 255%>
<%= f.submit %>
<% end %>
</div>
1)My Scribble model has min and max character length validation, now how do I print the error messages here. If it is an instance variable I know how to print, but this is a symbol.
2) This code is present in the application.html.erb. I am not able to understand how do I move it into a view of Scribble controller other than appliation. Problem is this form is not independent, it is a part of action index display of controller Scribbles,(and the form should be displayed always) and action index is already doing listing of scribbles.
Controller
def index
#scribbles = Scribble.order("scribbles.scribble DESC").all
end
def show
end
def new
end
def create
#scribble = Scribble.new(profile_params)
#scribble.likes =#scribble.dislikes =#scribble.shares=0;
#scribble.save
#scribbles = Scribble.order("scribbles.scribble DESC").all
render :index
end
Here how i out-put any errors or validation messages:
Controller:
def create
#scribble = Scribble.new(profile_params)
#scribble.likes =#scribble.dislikes =#scribble.shares=0;
if #scribble.save
flash[:notice] = "Scribble is successfully created"
redirect_to root_url
else #
render 'index'
end
end
Views:
Create a partial to show error messages if any e.g _error_messages.html.erb
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert callout text-center" data-closable>
<p><strong>This form contains <%= pluralize(object.errors.count, 'error') %>.</strong></p>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<button class="close-button" aria-label="Dismiss alert" type="button" data-close>
<span aria-hidden="true">×</span>
</button>
</div>
</div>
<% end %>
Render errors:
Now you can call <%= render 'layouts/error_messages', object: #scribble %> and put it anywhere in your views to render the errors validation. note: the object is passed, so it can be re-use to any form. credits to Hartl Tutorial.
Two models, Organization and User, have a 1:many relationship. I have a combined signup form where an organization plus a user for that organization get signed up.
The problem I'm experiencing is: When submitting invalid information for the user, it renders the form again, as it should, but the error messages (such as "username can't be blank") for the user are not displayed. The form does work when valid information is submitted and it does display error messages for organization, just not for user.
How should I adjust the code below so that also the error messages for user get displayed?
def new
#organization = Organization.new
#user = #organization.users.build
end
def create
#organization = Organization.new(new_params.except(:users_attributes)) #Validations require the organization to be saved before user, since user requires an organization_id. That's why users_attributs are above excluded and why below it's managed in a transaction that rollbacks if either organization or user is invalid. This works as desired.
#organization.transaction do
if #organization.valid?
#organization.save
begin
# I executed next line in debugger (with invalid user info), which correctly responds with: ActiveRecord::RecordInvalid Exception: Validation failed: Email can't be blank, Email is invalid, Username can't be blank, etc.
#organization.users.create!(users_attributes)
rescue
# Should I perhaps add some line here that adds the users errors to the memory?
raise ActiveRecord::Rollback
end
end
end
if #organization.persisted?
flash[:success] = "Yeah!"
redirect_to root_url
else
#user = #organization.users.build(users_attributes) # Otherwise the filled in information for user is gone (fields for user are then empty)
render :new
end
end
The form view includes:
<%= form_for #organization, url: next_url do |f| %>
<%= render partial: 'shared/error_messages', locals: { object: f.object, nested_models: f.object.users } %>
<%= f.text_field :name %>
# Other fields
<%= f.fields_for :users do |p| %>
<%= p.email_field :email %>
# Other fields
<% end %>
<%= f.submit "Submit" %>
<% end %>
The error messages partial is as follows:
<% object.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
Update: Following the steps from Rob's answer I arrived at the errors partial below. This still does not display error messages for User. I added debugger responses inside the code below and for some reason nested_model.errors.any? returns false, while the debugger inside the controller (see above) does return error messages for user.
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
</ul>
</div>
<% end %>
<% if defined?(nested_models) && nested_models.any? %>
# Debugger: responds with "local-variable" for "defined?(nested_models)" and for "nested_models.any?" returns true.
<div id="error_explanation">
<ul>
<% nested_models.each do |nested_model| %>
# Debugger: "nested_model" has the same values as "nested_models.any?", as you would expect. But for "nested_model.errors.any?" it returns false, which it shouldn't.
<% if nested_model.errors.any? %> #Initially had "unless nested_model.valid?" but then errors for User are immediately displayed on loading the form page (new method).
<ul>
<% nested_model.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
</ul>
<% end %>
<% end %>
</ul>
</div>
<% end %>
Try adding validates_associated :users under your has_many :users association in Organization.
http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_associated
Did you code successfully create a person during the rescue block?
rescue ActiveRecord::RecordInvalid => exception
# do something with exception here
raise ActiveRecord::Rollback
#organization.users.build if #organization.users.blank?
render :new and return
This code looks like it will create a new empty User regardless of incorrect validations. And render new will simply return no errors because the user was successfully created, assuming Organization has no Users.
The control flow of this method has a few outcomes, definitely needs to be broken down some more. I would use byebug and walk through the block with an incorrect Organization, then incorrect name. Then an empty Organization with incorrect User attributes.
organization has_many :users and user belongs_to :organization
organization.rb
accepts_nested_attributes_for :users
new.html.erb
<%= form_for #organization, url: next_url do |f| %>
<%= render 'shared/error_messages', object: #organization %>
<%= f.text_field :name %>
# Other fields
<%= f.fields_for(:users,#organization.users.build) do |p| %>
<%= p.email_field :email %>
# Other fields
<% end %>
<%= f.submit "Submit" %>
<% end %>
In controller
def create
#organization = Organization.new(new_params)
if #organization.save
flash[:success] = "Yeah!"
redirect_to root_url
else
render :new
end
end
This is very related to this question. The key is that <%= render 'shared/error_messages', object: f.object %> is, I assume, only calling the .errors method on the object it is passed (in this case, organization).
However, because the user errors reside with the user object, they won't be returned and therefore will not be displayed. This requires simply changing the view logic to also display the results of .errors on the various user models. How you want to do so is up to you. In the linked thread, the accepted answer had the error message display code inline instead of in a partial, so you could do it that way, but it would be somewhat redundant.
I would modify my shared/error_messages.html.erb file to check for another passed local called something like nested_models. Then it would use that to search the associated models and include the errors on that. We just would need to check whether it is defined first so that your other views that don't have a nested model won't cause it to raise an error.
shared/error_messages.html.erb
<% if object.errors.any? %>
<div class="error-messages">
Object Errors:
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% if defined?(nested_models) && nested_models.any? %>
Nested Model(s) Errors:
<ul>
<% nested_models.each do |nested_model| %>
<% unless nested_model.valid? %>
<li>
<ul>
<% nested_model.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</li>
<% end %>
<% end %>
</ul>
<% end %>
</div>
<% end %>
Then you would just need to change a single line in your view:
<%= render partial: 'shared/error_messages', locals: { object: #organization, nested_models: #organization.users } %>
Looks like you have a lot of untestable logic in your controller. Looks like for you logic will be better to use simple FormObject pattern.
https://robots.thoughtbot.com/activemodel-form-objects
Any idea why the messages hash would be empty in this situation?
I have these validations:
validates_presence_of :part_number
validates_uniqueness_of :part_number
a simple create:
if #part.save
puts 'saved'
redirect_to new_v2_path
else
puts 'not saved'
flash[:error] = "There was an error while updating the part."
redirect_to new_v2_path(#part)
end
in the view:
<% if #part.errors.any? %>
<div id="error_explanation" style="color: red;">
<h2><%= pluralize(#part.errors.count, 'error') %> :</h2>
<ul>
<% #part.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
the error object:
...l, flush_part: nil>, #messages={}>
In your failed validation branch you are redirecting instead of rendering, generating a new request to :new_2 action and instantiating a new #part object, with no value assigned and thus errors. You need to use render instead.
Change this line:
redirect_to new_v2_path(#part)
To this one:
render :new # or new_v2 or whatever action name you have.
I'm trying to create a form, where I can upload an CSV file to import or to preview before import it.
In my form I have:
<%= form_for(#contact_import, :remote => true) do |f| %>
<% if #contact_import.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#contact_import.errors.count, "error") %> prohibited this import from completing:</h2>
<ul>
<% #contact_import.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.file_field :file %>
</div>
<%= f.submit "Import" %>
<%= f.submit "Preview", :name => 'preview' %>
and in my controller:
def create
#contact_import = ContactImport.new(params[:contact_import])
if params[:preview]
logger.debug "Let's preview the contacts:" + params.inspect
#contacts = #contact_import.update_preview
#contact_attributes = ContactImport.mapping_attributes
#I should now update the preview div
else
logger.debug("Got the commit" + params.inspect)
if #contact_import.save
redirect_to root_url, notice: "Imported contacts successfully."
else
render :new
end
end
end
How can I update the view to show the preview contacts, by uploading the CSV file?
Note: The CVS file processing at the moment is at the model and had been omitted.
I'd take them to another version of the new page, parse the file and fill the contact_import object - prepare the page with hidden variables to be submitted to the create page.
You can simply look for this button push and render the preview page, using the generated #contact_import generated from the file
def create
#contact_import = ContactImport.new(params[:contact_import])
if params[:preview]
render :preview
elsif #contact_import.save
redirect_to root_url, notice: "Imported contacts successfully."
else
render :new
end
end
preview.html.erb is similar to new.html.erb, but with hidden inputs and back button. Posting from preview will also go to create, but should not cause any error conditions.
I don't believe you'll need a new route - just render :preview instead of :new in this case.
Alright, I've got to be missing something simple.
I'm using a partial "/shared/_error_messages.html.erb" to handle
<%= render 'shared/error_messages', object: f.object %>
in my forms (one for adding programs, one for adding metrics).
When I navigate to any form (/programs/new and metrics/new), the validation appears when the page loads.
The programs_controller and metrics_controller are structurally the same (swapping #metrics for #programs in metrics_controller):
#programs_controller.rb
def new
#programs = Program.new(params[:name])
if #programs.save
flash[:success] = "Program saved"
redirect_to "/program"
else
render 'new'
end
end
any ideas what might be causing this?
Here's the partial:
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
I don't know what the errors are but the problem is that your are saving your object in the new action. The saving generates the object errors; that is why you see them.
In a RESTful way, the new action should just instantiate a model and pass the object to your form. The form will submit it to the create action; where you should save your object and check errors.
Something like:
#programs_controller.rb
def new
#programs = Program.new
end
def create
#programs = Program.new(params[:program])
if #programs.save
# success
else
# failure
end
end