How work render action in controller? - ruby-on-rails

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.

Related

How are these to rails functions connected?

I am following this tutorial: Youtube rails tutorial
He is creating a basic website where people can add books, review them, rate them, delete them and so on. In the main controller script, he defines a function "new" to add a new book like so:
def new
#book = Book.new
end
def create
#book = Book.new(book_params)
if #book.save
redirect_to root_path
else
render 'new'
end
end
def book_params
params.require(:book).permit(:title, :description, :author)
end
He also uses a creat function for that. He has a "new" view named new.html.erb that actually shows the form where users can add a new book and this is related to the "new" function shown in the controller. The new.html.erb file only has this:
<h1>new book</h1>
<%= render "form" %>
I get most of it, what I don't get is, since the "new" function doesn't do anything and the "create" function is the one doing all the work for the "new" function, how are they related? I am asking this because the "new" function does not "call" the create function at all but the work the "create" function does is shown in the webpage. Are they connected through "#book"?
new action renders new.html.erb, which contains a form to create a new book. When a user clicks submit on that form, that new book is created with the create action.
I will recommend going to more tutorials and reading the guides to get a full understanding on how Rails works.
In Rails the new action displays the form for creating a resource.
create responds to when the user posts a form.
class BooksController < ApplicationController
# GET /books/new
def new
#book = Book.new
end
# POST /books
def create
#book = Book.new(book_params)
# ...
end
end
I am asking this because the "new" function does not "call" the create
function at all but the work the "create" function does is shown in
the webpage. Are they connected through "#book"?
You're fundamentally confused about how web applications and MVC work. Web applications respond to requests coming from a user. The server sends a a response and thats it. The program ends. The server does not sit around waiting for the user to click like a desktop program.*
They are not "connected" at all. new and create respond to a different kinds of requests at different paths. The are never both invoked in same request.
The new action shows and initialize the form. What you complete on it before you click on create button will send to the create action of the BooksController. This action will create a new Book object with the params, and save in DB.
This is MVC pattern. You should read about it: https://www.sitepoint.com/the-basics-of-mvc-in-rails-skinny-everything/

Rails proper way to render validation errors in controllers

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.

Passing parameters to the new controller action in Rails

I'd like to call a controller's new action to create a new object using an existing one to set defaults. So far, what I've done to to try this:
link_to 'New' new_item_path(t)
In a Wice Grid table where t is an instance of item.
The route that gets generated is /item/new.10 where the 10 is the :id of the item that I'd like to use as a default. I can get at it through request.referrer, but I'm wondering if there is a better/cleaner way to do this?
If you're setting defaults on a new object, you should consider setting those defaults at the database level within a migration. To keep your system RESTful, you should keep the 'new' action url independent of the object you're grabbing defaults from i.e. just /item/new.
If you want to set defaults from the existing object without applying at the database level, you should consider creating an instance variable in your controller action, and using that in your view to set the default values in your form elements. Another option would be to edit the object in a before_create callback within your model and set the fields there. Changing your route to accommodate this idea would be bad design IMO.
Passing the id to your controller as a param:
link_to "New Item Path", new_item_path(:default_item_id => item.id)
in your controller
#default_item = Item.find(params[:default_item_id])
request.referrer? You should be able to get at the id in you controller through params
Not sure if it's an issue but there should be a comma in between 'New' and the path specification
link_to 'New', new_item_path(t)
then in the controller you can setup strong params if you want to be thorough
ItemsController < ApplicationController
def new
# new action
end
def create
item = Item.new(item_params)
if item.save
flash[:notice] = 'Successfully created your item.'
redirect_to item #if there's an item#show page
else
flash[:alert] = 'Error: item not created.'
render :new
end
end
private
def item_params
params.require(:item).permit(:attribute_1, :attribute_2, ...)
end
end
Hope this helps

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

Resources