Rails render named route with correct URL - ruby-on-rails

I have the following route in my rails application
match '/settings', to: 'Users#edit', as: 'settings'
And corresponding controller code
def edit
#user = current_user
end
def update
correct_user
#user = User.find(params[:id])
if !#user.authenticate(params[:old_password])
flash[:error] = 'Old password did not match'
redirect_to settings_path
elsif #user.update_attributes(params[:user])
flash[:success] = "Settings updated"
redirect_to settings_path
else
render 'edit'
end
end
My edit page is as of now just a password change page, and when I visit /settings I see the page I'd except. When I redirect_to settings_path, the url remains /settings, which is the behavior I want.
In my edit template, I have code to handle object errors and render them on the page. When render 'edit, this code is triggered if there was an object error. If I redirect to the page, however, the code is not there. So I need to call render 'edit' to see the errors.
However, calling render 'edit' causes the URL to change to /users/:id/edit, which is exactly what I don't want. I'd like the URL to remain /settings after calling render 'edit'. How can I achieve this?
NOTE: I've already searched SO and other parts of the internet but have found nothing that suits my needs. There are one or two SO topics with similar issues but they all use flashing and hacky redirect-based workarounds, which is not what I want.

You would want to set up your routes like this:
match 'settings': 'users#edit', via: :get
match 'settings': 'users#update', via: :put
Then the form should be declared like this:
<%= form_for #user, url: settings_path %>
In your controller, make sure you're calling render like this:
render action: "edit"

You probably have a bad link_to in your view, one using the old-style :controller and :action arguments separately instead of using settings_path.
Keep in mind that if there are two routes to the same controller and action pair, the first route defined has priority. Define your custom route first to ensure it's used by default.

Related

How too add extra params in redirect when an instance variable is passed?

#in update function
redirect_to #post
Here I want to add some additional params when redirecting. However, as I try to add
redirect_to #post, :mypara => 'abc'
the page redirected to does not have this parameter in address bar, so I tried to print is out using
<%= params[:mypara] %>
but still nothing could be seen.
Can anyone help me out?
You should use post_path like this:
redirect_to post_path(#post, :mypara => 'abc')
This:
redirect_to #post
actually translates to:
redirect_to post_path(#post)
If you want to pass extra parameters, you cannot use the short cut. You have to use post_path.

Create action redirects to the wrong page?

When I check to see if my error message is working correctly, I notice my redirect goes to /businesses instead of /businesses/new after it POST (create action). I am using the regular RESTFUL routes but I need it to go back to /businesses/new on POST when their is an error. How can I do this?
This is my current code:
def create
#business = Business.new(params[:business])
if #business.save
redirect_to :back, :notice => "You have successfully added a new business."
else
render :action => 'new', :notice => "Please try again."
end
end
You should not redirect if the validation is failed. Here is what i should do to stay on the same page.
def create
#business = Business.new(params[:business])
if #business.save
redirect_to businesses_path
else
render :new
end
end
It is actually not a redirection. If you chech the html code generated for your form, you will see something like this:
<form accept-charset="UTF-8" action="/business"
class="new_business" id="new_business" method="post">
This means that the requested page to load is not the "/business/create" but the "/business". That is why you see it in the browser's address bar. This has nothing to do with your redirection in the create action. There you just render the previous form. This is not an error, it is actually working, maybe a little confusing, but that is not much of a problem, I think. With some javascript you can correct it if really needed.

Rails validation conditional redirect

I am currently having an issue with how Rails is performing and responding to a validation result. I have a user registration form. The user could hit this form in two different places. They could hit the form from the homepage or from users/new. Both forms will post to the same place as I am trying to keep it DRY.
The users/new page works as is expected. If the user has a validation issue it will return and populate the form. Where I get a problem is on the home page. If a user has a validation issue it now redirects to the users/new page. I would much prefer that when on the home page I would return the user to that same page and show the validation results there. Is there a way in the controller to redirect to the form the user was at?
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" } # I'm thinking I can do something here?
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
I have tried to change the render :action => 'new' line to redirect to the user url but it hasn't worked. Is there something I'm missing?
First, I would add querystring parameters to the URL it is posting to with the controller and action that it came from with something like this:
# Using form_tag
<%= form_tag user_path(#user, :controller_name => controller.controller_name, :action_name => controller.action_name) do %>
# Using form_for
<%= form_for #user, :url => user_path(#user, :controller_name => controller.controller_name, :action_name => controller.action_name) do %>
Then, you can update that line in the create action of your controller like this:
render '#{params[:controller_name]}/#{params[:action_name]}'
Update
I just realized that using the code above, will render the correct view the first time validation fails, but if validation fails a second time, it will try to render the users/create view. If this is the route you want to take, you should not use controller.controller_name, etc in the view, but assign #controller_name correctly and use that variable instead. However, this only adds to the 'overkill' comment made by Xavier.
Art's on the right track, but you can't use a redirect, as you need the instance variable #user that's set in your controller, which'll be lost on a new HTTP request (because ever request is handled by a new, clean controller instance).
But you can use the referer information yourself, and use that to pick the right page to render:
render :action => (request.referer =~ /\/users\/new/)? :new : :index
Note: Another answer popped up while I was posting that suggests adding the old controller / action fields to your form, but that seems like overkill to me - you already have all the information you need in request.referer.
Hope that helps!
Try redirect_to :back
It's a shorthand for redirect_to(request.env["HTTP_REFERER"])
oops, it only works for success. sorry
well, then you have to check inside the block (after format.html) where he came from (by looking at request.env["HTTP_REFERER"]) and render respective action.

How to modify the create & update method in RoR?

I know that the after the create or update methods is done, they have the method like this
respond_to do |format|
format.js
end
Since I change my form from remote form to non remote form, so I won't use the format.js anymore, I just want to refresh the page, after the user create/update a product, so I have this code:
respond_to do |format|
page.reload
end
But it don't work, so I try not to use respond_to do, I only have the page.reload. But it also show me the site like this:
http://localhost:3000/products/50
I just want to reload the page after I create/update, why I can't do it in this way?
The reload method reloads the browser's current location using JavaScript. I would suggest that you probably want to do a server-side redirect after creating or updating your resource. Something like one of these alternatives:
redirect_to product_path(#product) # Redirect to the 'show' page for the product
redirect_to products_path # Redirect to the products' index page
How can I assign the product_path? in routes.rb?
in routes.db you can either:
map.product 'products/:id', :controller => 'products', :action => 'view'
map.resources :product
map.resource :product
1) will give you product_path(123) and product_url
2) will give you product_path, new_product_path, edit_product_path and so on
HTH

How do I use redirect_to if I've got multiple controllers in different subdirectories?

I've created a small application to learn RoR. (Book database) It consists of a read-only area and a read-write admin area.
After I've got the admin functionality working first, I've moved the controller into a subdirectory and created the read-only controller.
Now when I'm updating a book in the admin area, the redirect_to function redirects to the read-only area.
What am I missing?
Here's the code I'm using:
class Admin::BooksController < ApplicationController
<snip>
def update
#book = Book.find params[:id]
respond_to do |format|
if #book.update_attributes params[:book]
flash[:notice] = "Book updated"
format.html { redirect_to #book }
format.xml { head :ok }
else
<snip>
end
end
end
<snip>
end
This update itself works but it redirects me to /books/1, but I'd want it to redirect to /admin/books/1. I could just hardcode the correct path, but I guess that's not very good style.
What would be the proper way?
PS: Please comment if you need further information.
You are telling it to redirect to book because you are using rails' built in magical recognition of what it should do with the #book object (which is build a url to show the book using the book controller.
format.html { redirect_to #book }
If you want it to go elsewhere you need to be explicit about where you want it to go using a hash for url_for
format.html { redirect_to :controller => 'admin/book', :action => 'show', :id => #book }
or use the paths like klew points out.
so for more detail -
redirect_to (#book) or
redirect_to book_path(#book)
are both shortcuts for this:
redirect_to :controller => book, :action => 'show', :id => #book.id
Rails creates for you url helpers based on your routes.rb. If you have namespace then you can use this:
admin_book_path(#book) # admin/books/2
admin_books_path # admin/books
edit_admin_book_path(#book) # admin/books/2/edit
and so on.
The other way is to use resource_controller it creates for you controller automaticaly and provides some ways to modify it if it's needed. It also gives you some useful url helpers
collection_path # admin/books
object_path # admin/books/2
When you use above helpers in views, than it generates url with namespace if you are in one, or without namespace otherwise.
resource_controller isn't perfect, but in most cases it works good and saves a lot of work.
You can also pass an array to redirect where the first element is a symbol representing the namespace, and the second the element the object.
redirect_to [:admin_book, #book]
You can also use this for form_for, link_to and any other helpers that require a path.

Resources