the f.error_messages form builder helper has been depreciated in Rails 3.
How do I get the error messages for a form with the opening tag:
<%= form_for Model.new %>
(I'm using Model.new because I want to be able to load an undefined multiple number of these forms onto a single page)
You can't. Not with a form builder like this one.
Basically, error messages are stored inside the object you use to build a form. If you build a new one every time, you get a clean object without errors.
What you need to do is persist the object filled in by the user between requests. Typically this is done by creating a new object in controller:
#model = Model.new
The essence of this is, that new view uses #model to render a form. And the trick is to have a possibility to render the same view in other actions that also provide #model. That said, if you do something like this in create:
#model = Model.new(model_params)
if #model.save
# success
else
render :new
end
It can render new view, because it assigns #model too; in this case, it will contain errors with messages and other stuff. All this is inside #model.errors – which is always empty in new action.
It's not that different for multiple forms, bear in mind that you always submit only one. You may switch to rendering an array of forms, in that case you could have #models array:
#models = [form1, form2, form3]
In that case, if saving fails, assign that array again and either replace the form the user tried to fill in (if you can identify it), ot prepend/append that form with errors to that array.
#models[index_of_submitted_form] = form_from_user #replace
#models << form_from_user # append
#models.unshift form_from_user # prepend
In Rails 3, you can access errors using #model.errors
<%= form_for #model = Model.new do |f| %>
<% #model.errors.full_messages.each do |msg| %>
<p><%= msg %></p>
<% end %>
# ...
# ...
# ...
<% end %>
Related
I am trying to understand the reasoning behind writing the line of code
#example = Example.new two times.
According to the answer below, it seems that the model object is instantiated twice but I don't understand why we need to instantiate the object for the new method.
Rails : How does "new" action called "create" action?
def new
#example = Example.new
end
def create
#example = Example.new(example_params)
if #article.save
redirect_to #example
else
render 'new'
end
end
You can't use the blank object because your view won't be able to build a form for example in that case. Usually people are building forms when they do that and to use rails form builders, the correct model object is required (in this case #example), otherwise form builder will never know about the attributes of that model object it has to build.
<%= form_for #example do |f| %>
Name: <%= f.text_field :name %>
<% end %>
In above, without #example, the form_for won't be able to figure out that it has to go to POST create action of Example. Nor will it be able to identify name field.
In case you want to remove #example = Example.new from new action, you will have to build the form on your own. But, follow the conventions, it's not recommended.
<%= form_tag('/examples') do %>
Name: <= text_field_tag :name %>
<%= submit_tag 'Submit' %>
<% end %>
In new method when we use #example = Example.new we initialise blank object of current model and #example is used to bind the form in views.
<%= form_for #example do |f|%>
if you see the html for above line it will be like
<form accept-charset="UTF-8" action="/examples" method="post">
#example in new basically bind the model attributes with form
when we user #example = Example.new(example_params) with again initialise the object with the form values i.e. values which user enter in the form and submit.
You can visit the following link to understand more http://guides.rubyonrails.org/routing.html
#example is not instantiated twice here. These are two different actions. Every time one of them is called, a new instance of the controller class is created. So #example only belongs to that instance.
You instantiate #example for #new so that it can be available in the view. Rails automatically share the instance variables from the controller actions with their corresponding views by copying them over. You do the instantiation in controllers because it is part of the controller's job. This way you have a nice separation of concerns: views don't need to bother with creating variables, only with presenting them.
You can of course do it this way:
##controller
def new
end
##new.html.erb
<%example = Example.new%>
<%=form_for example do |f| %>
##code omitted for brevity
And it will work. But this is not a good practice as you're mixing different logics in views.
Ok, So i'm trying to place the form found in the "_form.html.erb" in the "index.html.erb" of my ruby project crashes with the error
"First argument in form cannot contain nil or be empty"
<%= form_for(#customer) do |f| %>
I know that changing the #customer to Customer.new could fix this but I would like to know why this isn't necessary in one file and it is in another
Why is this happening and how do I make a form that will update the sqlite db on the index page.
#customer is a variable that must be created somewhere in the corresponding controller action. If your #index controller action defines a variable by that name, then you'll be able to use it in the view template; otherwise you'll need to create it like this:
#customer = Customer.new # (or whatever the value is)
When Rails processes a request, it just executes a big (and complex) lump of code that's created from a bunch of different files. First it executes the appropriate controller action, then it executes any Ruby code found inside the corresponding view template. So any variable (or any method name) that is used in the view template, was first defined at some point before that: either in the controller action, or in one of Rails' countless built-in helper files.
When I am using form_for in a index or show page I like to do is set it to new
<%= form_for Customer.new, url: {controller: "customers", action: "create"} do |f| %>
...
<%= f.submit %>
<% end %>
that way there is a object to be created, I also I like to pass in the controller and the action.
I have a index page and wish to display a form from a different controller that I created on the index page. I have 3 folders in my view. In the folder called index, I have a html file called index and in the folder called job I have form.html and show.html.
so in my index.html I have
<h1>Complete Job Applications</h1>
<%= render 'job/form'%>
When I run the code I receive this error First argument in form cannot contain nil or be empty. I think I know why but am not too sure. Instead of displaying the form so the user can fill out all the needed text fields so it can be submitted it's submitting it right away (just a guess though). Does anyone have any ideas to how I can display a form from another controller on my index page
You will need to initialize the object that is passed to the form, for example, lets say your view looks like this:
# job/form
<%= form_for(some_model_instance, method: :get) do |form|
...
<% end %>
To render that form from another controller's action, make sure to initialize some_model_instance before rendering the partial, so:
# controller which has the index action
def index
#some_model_instance = SomeModel.where(id: 1).first
end
# index.html.erb
<h1>Complete Job Applications</h1>
<%= render partial: 'job/form', locals: { some_model_instance: #some_model_instance } %>
I have a form where I'd like to create a parent record and a child record at the same time. For a simple example let's say its a Company with the first Employee.
in my controller I do something like:
def new
#company = Company.new
#company.employees.new
end
and in my view this:
<%= form_for(#company) do |form| %>
<div>
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<%= form.fields_for :employees do |employee_form| %>
<div>
<%= employee_form.label :name %>
<%= employee_form.text_field :name %>
</div>
<% end %>
<% end %>
and back in my controller again:
def create
#company = Company.new(params[:company])
#company.employees << Employee.new(params[:company][:employees_attributes]["0"])
# save stuff
end
Question 1:
I couldn't get the employee collection on the company to be populated with the single employee created in the form. When I looked at the params I found the [:employees_attributes]["0"] stuff.
What I have works, but is there a cleaner way to do this?
Question 2:
If the validation doesn't pass for the employee I get a generic "Employees is invalid" instead of the Name required validator message. I get I am calling save on the collection and rails is doing its best to bubble a validation error up, but is there a cleaner way to do this so I can get the errors specific to the employee?
In Short
How can I clean this up so the related models are created automatically from the params, and so that I get the validation messages for a single employee.
Thanks for looking.
1) fields_for arranges for the child objects attributes to be nested inside the parent objects attributes in the params hash that gets sent back to the controller action. To get Rails to automatically update the child objects tell the parent model to accept nested attributes using the accepts_nested_attributes_for declaration.
2) There is an errors object for every ActiveRecord object. Loop through the errors list and display the messages.
Best way to achieve this is to create a partial and a view helper method that will take render the errors for you. then replace the generated errors messages in the forms with a call to your render_error_messages method. You have all the code to do this already in the generated forms. You just need to refactor that code into a partial, create the helper - which should accept an array of model names as a parameter then do what you want with the info. Wither render a partial for each model or render a partial that will deal with child objects as well as the parent object. Totally your call.
3) Change your new action to build rather that create a new child object so instead of
def new
#company = Company.new
#company.employees.new
end
do this
def new
#company = Company.new
#company.employees.build
end
4) Watch those Railscasts to see how accepts_nested_attributes works
http://railscasts.com/episodes/196-nested-model-form-part-1
and
http://railscasts.com/episodes/197-nested-model-form-part-2
Update
So how does the above information leave you in relation to your questions.
1) What I have works, but is there a cleaner way to do this?
You've fixed the new action as per point 3 above right? Now your create action can look like this
def create
#company = Company.new(params[:company])
# save stuff
end
Which is much cleaner as it has reverted to the original generated create action.
You may not think that's much of an update and therefore not that much cleaner. Well in isolation you'd be right. But consider that you could add as many relationships as you like ad add as many fields_for declarations as you like nd you could turn the user -> employee relationship into a has_many (I know that you wouldn't). You could do all that and your create and update actions stay EXACTLY the same and that's why it's cleaner.
2) is there a cleaner way to do this so I can get the errors specific to the employee?
Given my response in point 2 above you know that there is an errors object on the employee object as well as on the user object right? You also know now that you can loop through that errors object to get the messages right?
So you could do this
<% if #user.employee.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.employee.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.employee.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
At the risk of repeating myself I'll just say that you should refactor your error messages view code into a partial that will take any object as a parameter then you can call it from any view thus enabling you to change the styling and the functionality for all your forms.
Hope that's clearer
I have the following model:
class Activity < ActiveRecord::Base
has_many :clientships, :dependent => :destroy, :after_add => :default_client_info
accepts_nested_attributes_for :clientships, :allow_destroy => true
end
In my controller, if I perform the following
def new
#activity = IndividualActivity.new(params[:activity])
#activity.clientships.build(:client => Client.first)
...
end
and then save the form, it creates the relevant params and submits successfully.
However, if I chose to call the following through a remote link
#activity.clientships.build(:client => Client.last)
the view is updated with the new clientship record but when I submit the form, the params[:activity] is not created for the second nested attribute. (Why not!?)
This is the view:
%h1 Create a new Activity
- form_for #activity do |f|
%div
= render "activities/client_selector", :f => f
%div
= f.submit "Save!"
Here is the remote_link's controller action
def add_client
#activity = IndividualActivity.new(session[:individual_activity])
# Refresh client
#activity.clientships.build(:client => Client.find(params[:client_id]))
respond_to do |format|
format.js
end
end
This is the add_client.html.js:
page.replace_html "selected_clients", :partial => 'activities/clients'
This is the activities/clients partial:
- form_for #activity do |f|
- f.fields_for :clientships do |client_f|
%tr
%td= client_f.hidden_field :client_id
%td= client_f.object.client.full_name
Does anyone know how I can troubleshoot this further? I seem to have come to a dead-end with my debugging... One thing to note, there is a double use of the following form_for used in new.html.haml and the activities/clients partial (is this problematic?)
- form_for #activity do |f|
I am on rails v2.3.5
Thanks
You ask about debugging, so the first step may be looking at the server log (log/development.log).
There you should see the "params" hash.
Maybe your params contain "activity"=>{"client_id"=>..} instead of "client_id"=>.. ?
Also look at the generated HTML page - use a Firebug or just use a "view source" method of your browser. Look, especially, for input names.
If everything looks OK, put a few debug calls in your action, and look at the development.log for some database activity - do the SQL queries look like they are doing what you want?
In your question there is no 'save' method. The 'build' method does NOT save the created record. Maybe this is your problem?
def add_client
logger.debug "Creating Activity"
#activity = IndividualActivity.new(session[:individual_activity])
logger.debug "Building clientship"
# Refresh client
#activity.clientships.build(:client => Client.find(params[:client_id]))
logger.debug "#activity = #{#activity.inspect}"
# Maybe you were missing this part of code?
logger.debug "Saving #activity"
#activity.save! # use a ! to easily see any problems with saving.
# Remove in production and add a proper if
logger.debug "Saved. #activity = #{#activity.inspect}"
respond_to do |format|
format.js
end
end
You should create a functional test (in case you haven't already) and ensure that if you send proper parameters, your action works as intended.
The test will narrow your search. If the test fails, you know you have a problem in the action. If the test is OK, you need to ensure the parameters are sent properly, and you probably have the problem in your view.
UPDATE:
You said you have TWO forms on the page. This may be the problem, since only one form may be sent at a time. Otherwise it would need to work in a way which can send two requests in one request.
First thing (useful in all similar problems): validate whether your page has correct HTML structure - for example http://validator.w3.org would be a good start. Try to make the code validate. I know that some people treat a "green" status as a unachievable mastery, but just it's really not so hard. With valid code you may be sure that the browser really understands what you mean.
Second: Place all your inputs in a single form. You have problems with nested attributes. For start, try to manually insert inputs with name like <input name="activity[clientship_attributes][0][name]" value="John"/>, and for existing clientships ensure that there is an input with name = activity[clientship_attributes][0][id].
This is the way nested attributes are handled.
Your view may create such fields automagically. This construction should be what you need: (it worked in one of my old project in rails 2.x, I have just replaced the names with ones you use)
<% form_for(#activity) do |f| %>
<p><%= f.text_field :activity_something %></p>
<% #activity.clientships.each do |clientship| %>
<% f.fields_for :clientships, clientship do |cform| %>
<p><%= cform.text_field :name %></p>
<p><%= cform.text_fiels :something %></p>
<% end %>
<% end %>
<% end %>
If you really want to use a partial there, don't create a new form in the partial. Use only the parts of above code.
To pass a variable to the partial, use :locals attribute in the place where you call render :partial:
<%= render :partial => 'clientship', :locals => {:form => f} %>
Then, in your partial, you may use a local variable form where you would use f outside of the partial. You may, of course, map the variables to the same name: :locals => {:f => f}