I have a form for a user to create a question (in additon to user model, there's a question model, with nested answers) on their profile page. It submits from the users profile page /views/users/show.html.erb to the create action of the questions_controller.rb. If it doesn't validate, I think the default for Rails is to render the form(with the invalid information in the form for the user to edit). However, since I'm submitting the form for the question model from the users profile page the prepopulation isn't happening upon failed validation; the user is forced to enter all the information in the form again. Is there a way in this context to get the form on the users show page to fill out with the information that was entered prior to submission?
questions_controller
def create
@question = current_user.questions.build(params[:kestion])
if @question.save
redirect_to current_user, :notice => "Successfully created question."
else
###render :action => 'show'
redirect_to current_user
end
end
Update I've changed the end of the create method too
Redirect ( : back ), :notice => "something went wrong.try again"
But I still can't get the form to populate, and the validation error messages aren't showing either, only the flash notice.
Update The show method of the users controller creates the new Question (along with the user)
def show
@user = User.find(params[:id])
@question = Question.new
3.times {@question.answers.build}
end
The /views/users/show.html.erb
<%= form_for @question do |f| %>
<% if @question.errors.any? %>
<h2><%= pluralize(@question.errors.count, "error") %> prohibited this question
from being saved: </h2>
<ul>
<% @question.errors.full_messages.each do |msg| %>
<li> <%= msg %></li>
<% end %>
</ul>
<% end %>
<p>
<%= f.label :content, "Question"%>
<%= f.text_area :content, :class => 'span4', :rows => 1 %>
</p>
<p>
<%= f.label :link, "QuoraLink" %>
<%= f.text_field :link, :class => 'span4', :rows => 1 %>
</p>
<%= f.fields_for :answers do |builder| %>
<p>
<%= render 'answer_fields', :f => builder %>
</p>
<% end %>
<p><%= link_to_add_fields "Add Answer", f, :answers %></p>
<p><%= f.submit %></p>
<% end %>
the answer_fields partial rendered from the questions partial
<p class="fields">
<%= f.label :content, "Answer..." %>
<%= f.text_field :content, :class => 'span3', :rows => 1%>
<%= f.label :correctanswer, "Correct" %>
<%= f.check_box :correctanswer, :class => 'span1' %>
<%= link_to_remove_fields "remove answer", f %>
</p>
Currently, in views/users/show.rb you do
@question = Question.new
that creates an empty new question. Then you populate the forms with this empty model. What you could do instead is:
if session[:question]
@question = @user.questions.new(session[:question])
session[:question] = nil
@question.valid? # run validations to to populate the errors[]
else
@question = Question.new
end
Now all what's left to do is populating session[:question] in your questions_controller before redirecting to :controller=>"users", :action=>"show". Something like:
if @question.save
redirect_to current_user, :notice => "Successfully created question."
else
session[:question] = params[:question]
redirect_to current_user
end
You may need to work on serialization/deserialization additionally for populating/using session[:question]. I didn't try to compile, so am not sure.
All this is needed because when you do redirect_to your processing of the user request ends, the user browser gets a redirect status code from your server and goes for another page, sending you a new request (which lands on the path, and eventually controller/action, to which you redirected to). So, as soon as you return from the request processing, all your variables are lost. For the next request you start from scratch.
The render :action => "show"
approach (that was in the original scaffold and that you commented out) worked because you didn't return back to user but simply rendered the template with a specific name using the variables you already had in place (including @question, on which 'save' was called and failed, and thus internally validations were called and populated the errors object).
Actually, that reminded me that you may want to use another approach. Instead of passing parameters through session[] and redirecting to UsersController, you may want to populate all required variables and just render the view from that controller. Like below:
if @question.save
redirect_to current_user, :notice => "Successfully created question."
else
@user = current_user
render "users/show"
end
Firstly, the reason that using redirect_to
instead of render
doesn't repopulate the form is that when you redirect_to
, the controller logic for the action is run, whereas using render
ignored the controller logic.
So when you render :action => 'show'
(the "default" behaviour), it renders show.html.erb
, with @question
set like this:
@question = current_user.questions.build(params[:kestion])
When you redirect_to current_user
, it renders show.html.erb
with @question
set using the code in your show
action:
@question = Question.new
3.times {@question.answers.build}
This is why you get a new (blank) form, instead of a pre-populated one.
Is it really that important that you use redirect_to
? If it is, you'll need to get your show
method to do the validation. For example, you could rewrite your show
method to something like:
def show
@user = User.find(params[:id])
if params.has_key?(:kestion)
@question = @user.questions.build(params[:kestion])
else
@question = Question.new
3.times {@question.answers.build}
end
end
and then make your form point at that page, with something like:
<%= form_for(@question, url: users_path(@question.user) do |f| %>
...
<% end %>
(depending on how your routes are set up and named). Of course, by that point the whole thing become horribly un-RESTful, a bit of a mess, and definitely not the Rails way of doing things. (The other, worse option would be to redirect back and pass the params through a get query.) In my opinion, you lose a lot for a minor gain, and I'm not sure that I'd really recommend it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With