Rails proper way to render validation errors in controllers - ruby-on-rails

I read the documentation on Rails Validation, but didn't see much about how to work with errors in the controller.
Question -- Are render :new and render :edit when .save fails the proper way to show the errors after submitting the form?
Also, is there a way that I can keep the polls/new and polls/edit routes the same as when the form was initially loaded?
In my view I have the #poll.errors.any? ... #poll.errors.full_messages.each do ... block. These errors are successfully called if I have the following code in my controller, with render :new being the main point of interest:
class PollsController < ApplicationController
def create
#poll = Poll.create(poll_params)
if #poll.save
redirect_to polls_path
else
render :new
end
end
The form shows up, but my route changes from polls/new back to polls and none of my options are built. I also tried redirect_to new_poll_path but of course that starts from scratch and shows no errors.

Take the following scenario: A user navigates to the form in your new action, enters some data, and presses "submit". By default (i.e. assuming you're using restful routes), rails will submit your form to the create action in your controller. Assume that the user entered some data that was incorrect and would cause #poll.save to fail. In your case, this code would run:
render :new
In this case, you have told rails to simply render the new partial again. However, since you've told rails to render the new partial from your create action, you will see /polls in your url bar since /resource_name is how rails implements the create portion of its restful routing system (and you've defined your resource to be called "polls".
To answer this question:
Are render :new and render :edit when .save fails the proper way to show the errors after submitting the form?
The short answer is yes. In rails, this is typically the preferred way of showing errors to users who enter incorrect data into a form. However, if you wanted to ensure that /edit or /new was in the url bar after an unsuccessful form submission, you would need to replace:
render :new
with
redirect_to new_poll_path, alert: #poll.errors.full_messages
This solution will cause the errors in your #poll object to be displayed in your flash messages area (assuming you have that set up). However, I don't personally like this solution much because flash messages typically aren't intended to be used this way. This begs the question:
Why can't I use #poll.errors.full_messages after a redirect?
The reason for this is because your #poll object (in the create action) is carrying the error messages. As soon as #poll.save fails, your #poll object will be carrying the error messages that caused the .save method to fail. If you were to redirect away from the create action back to the new or edit actions, the #poll object with the errors would be replaced by a new #poll object as defined in either the new or edit actions in your controller. Therefore, using render allows you to give the #poll object with the error messages back to the appropriate action so that the error messages can be displayed.

Related

How work render action in controller?

I have method in controller like this:
def create
#player = Player.new(player_params)
if #player.save
flash[:success] = "Player created"
redirect_to player_path(#player)
else
render 'new'
end
end
And I have also test:
it "add players without nickname" do
visit new_player_path
click_button "Add player"
current_path should eq new_player_path
end
But after call render method my current path is:
http://localhost:3000/players
not
http://localhost:3000/players/new
But the layout is from players/new. Why?
And what should be my test? In test I just want to check if user don't type in nickname in nickname filed application return to create user page (players/new path).
Actually when there is any errors then it moves from new to create action, so the path becomes http://localhost:3000/players, and the render just renders the new action's template here. Now the main question is why so?
As in the new action you have submitted the form with some data to the create action. Now if suppose it sends you back to new action then it would have to redirect you back to that page, so what will redirect do? It will make rails to loose all the form data which you have submitted and the data would never persist. So instead what is done is you remain on the create action which has the url: http://localhost:3000/players and it renders the new action which means the new action's template (or you can say form). It doesn't redirects it just renders it in the same one.
Now the question is how does the data persist? The data persists with the object. This line #player = Player.new(player_params) creates a new object of the Player class with the attributes you have passed in the form. Now as you remain in the same action and the object is persisted so the data on the form is also persisted.
If you want to test it then in your code replace this:
render 'new'
with:
redirect_to new_player_path
and you will notice the data would never persist unless you pass that explicitly.
Hope this helps.

Can I override just part of an ActiveAdmin controller action using `super` + custom redirect?

I've registered a Widget in ActiveAdmin and want to change the redirect that takes place after creating a new one. So that I can accomplish various things with Javascript, I've created a custom form for creating/editing them such that in /admin/widget.rb I have this:
form do |f|
render "create_or_edit_widget"
end
I want to modify the basic Admin::WidgetsController#create action to change where the user is redirected after successfully creating one. I can fill out the rest of the custom action to complete this, except I don't know how to handle a case where the .save fails and the user is redirected back to the form with the formtastic inline error messages. I know how I could do this if I wanted the normal Rails form behavior of creating a list of error messages but not enough about Formtastic to copy its behavior. So far I have this:
controller do
def create
#widget = Widget.new(params[:widget])
if #widget.save
redirect_to admin_widgets_path, notice: "Successfully created Widget."
else
redirect_to :back
end
end
end
I was wondering if I can somehow user super and then only change the redirect path after successful creation instead of having to write out the entire action. If that's not possible, can anyone tell me where in the ActiveAdmin GitHub I'd be able to find the standard #create action so I can copy it out and change the one part?
Yes, you can do that. Here is a working code from my application using super and just changing the redirection
def create
super do |format|
redirect_to admin_submission_discussion_path(id: resource.discussion.slug, submission_id: resource.discussion.client_application.slug) and return if resource.valid?
end
end

when to render with instance variable, and when to redirect?

My new and edit pages depend on an #important_data instance variable that is not used in the create and update actions.
As a result my page can't render the new page upon failure.
def create
#my_object = MyObject.new(params[:my_object])
if #my_object.save
redirect_to root_path
else
render action: "new"
#this can't render because the page asks for an #important_data variable that's not defined.
end
end
Which of the two solutions below should I choose?
What are the advantages/disadvantages of each?
OPTION 1: declare #important_data prior to render
def create
#my_object = MyObject.new(params[:my_object])
if #my_object.save
redirect_to root_path
else
#important_data = ImportantData.all
render action: "new"
end
end
OPTION 2: Redirect
def create
#my_object = MyObject.new(params[:my_object])
if #my_object.save
redirect_to root_path
else
redirect_to new_my_object_path
end
end
When you use render, you're using #my_object with the attributes updated from params[:my_object]. Most of the case, this is what you want. When you show the page to the user, you want to preserve the changes they made to the form and show them the errors.
When you use redirect, you're doing a different and additional request so the parameters submitted from the form are not preserved (unless you pass them in your call to redirect and build them up in the controller action).
So in most cases, you'll definitely want to declare #important_data when the validation fails. I can't think of a case where you'd want to redirect.
Offcourse, the Option1 will work best, since you are rendering new in case of errors only. Also, redirection li'l bit mess up the user experience, it will take a bit long to render the page again with same query #important_data = .... running again.
I think you should use OPTION1
So the place where redirect_to should be used is when you're doing a HTTP POST request and you don't want the user to resubmit the request when it's done (which may cause duplicate items and other problems).
In Rails, when a model fails to be saved, render is used to redisplay the form with the same entries that was filled previously. This is simpler because if you use redirect, you'll have to pass the form entries either using parameters or session. The side effect is that if you refresh the browser, it will try to resubmit the previous form entries. This is acceptable because because it will probably fail the same way, or if it's successful now, it was what the user should expect in the first place anyway.
The above answer is referenced from: Are redirect_to and render exchangeable?

Rails : Is it possible to put a "create" form on a "index" or "show" view?

Hi I'm trying to work on my first rails project and was wondering if it was possible to combine the show or index view (of photo albums) with a form that creates more photo albums. Would the URL not have the correct parameters to do this? Would I be able to set the action on the form to make it "create" and redirect_to the index page again on success?
Sure you can do this, just put the form code on whatever page you want to create/update from and it will work.
The only issue is where the action would redirect you to after a successful or unsuccessful create/update: usually from the new page, you would redirect to the newly-created record (show action for the new record) on success and back to the new action on failure (with the errors on the form fields). If you want to create/update records from different pages, and have the action redirect to different pages in each case, then you'll have to do just a bit more work.
On possibility would be to add a hidden parameter to the form with the action to redirect to, and make the action check for it and redirect accordingly. For example:
VALID_REDIRECT_ACTIONS = ["show", "index"]
def create
...
if #photo.save
flash[:success] = "Photo successfully created!"
if VALID_REDIRECT_ACTIONS.include?(params[:redirect])
redirect_to params[:redirect]
else
redirect_to #photo
end
else
...
end
end

Rationale behind render :new instead of redirect_to new for POST create?

Given a rails RESTful controller
class WidgetsController
def new
#widget = Widget.new
end
def create
#widget = Widget.new(params[:widget])
render :new and return unless w.save
redirect_to widget_path(#widget)
end
end
Other than to capture the parameters and render validation messages why is the convention to render :new instead of redirect_to new_widget_path if validation fails?
As URL will alter from /widgets/new to /widgets if validation fails doesn't this break the concept of REST?
Render will not call the controller action, so the instance variables that you have set in the create action will be taken to the new view.
this means that any validation errors persist in the new view...
redirect_to would run the controller action for new, and therefore create a new instance of your model... and you would loose the errors...
hope this helps
Could you do this instead of rendering the new action?
flash[:thing] = params[:thing]
redirect_to new_thing_path
Then in new
#thing = Thing.new(flash[:thing] || params[:thing])
This honestly seems nicer to me because otherwise if you have any logic in your controller that is required to render the new/edit actions, you have to repeat that logic (or create some before_filter or something) in update/create. I realize this involves one more request but aside from that it seems simple and safer to code (apart from the fact that you are breaking rails conventions) and it gives the user a valid url that is always the same for looking at/doing the same thing.
If they refresh they'll lose their values but that is true of any form before being submitted. And that seems to make more sense to me than refreshing resulting in a resubmit warning which is pretty strange. How is a user supposed to clear the form for example?
This rails-ism has always bugged me. Are there are any serious issues with this that I'm not considering? Do other application frameworks all do the same?
The thing with render is that it will not cause another request to be generated. It will go to the corresponding view and display it. With redirect, however, a redirect request to the browser will be generated, which will cause another request to your server.

Resources