Looking for a clean way to render the action and controller of the request referrer in case a validation fails for a resource controlled by another controller. This is because the resource can be created from multiple different views.
I have done along the lines of this so far:
if #resource.save
redirect_to resource_path(#resource)
else
request_referer_controller = Rails.application.routes.recognize_path(request.referrer)[:controller]
request_referer_action = Rails.application.routes.recognize_path(request.referrer)[:action]
search_param = Rails.application.routes.recognize_path(request.referrer)[:search_param]
render "#{request_referer_controller}/#{request_referer_action}", search_param: search_param if request_referer_controller.include? "search"
render "#{request_referer_controller}/#{request_referer_action}" if request_referer_controller.include? "profile"
end
But to me that's ugly somehow. There must be something built into rails to determine and render the previous request controller, action and any params right?
I'm getting used to Rails just having everything built in.
Related
I know this is possible but I want to render a controller / action in response to a different action like in my routes.rb, I have:
get ':handle' => 'handler#index'
and in handler_controller, I have:
def index
handle=Handle.where('handle=?',params[:handle]).first
if handle.handle_type=='Location'
# call location / show with id from handle
else
render text: "not found"
end
end
How would I do this? I should mention, I don't want to do a redirect.
Maybe this can help:
def action_that_calls_one_from_another_controller
controller_you_want = ControllerYouWant.new
controller_you_want.request = request
controller_you_want.response = response
controller_you_want.action_you_want
end
You can solve this by using an "internal redirect" approach.
Inside rails this can be done via middleware that calls #app again and tracks redirect loops. See tutorial here
Also can be done by utilizing nginx's X-Accel-Redirect: make a regular routes for referenced resources, but instead of regular redirect issue a X-Accel-Redirect header, for user it will be like your page rendered without redirect.
In my app I have a /thanks page that users were originally redirected to when completing a certain action. Now I want to redirect them to this page after multiple kinds of events and render different partials based on what the event was. So I added this to the /thanks page:
- case #event
- when "reservation"
= render 'thanks_job_created'
- when "charge"
= render 'thanks_job_charged'
Then, in the the JobsController#create action, I changed redirect_to thanks_jobs_path to this:
redirect_to thanks_jobs_path(params.merge(event: "reservation"))
...and added #event = params[:event] to JobsController#thanks.
The behavior works as intended, but I've found that using params.merge this way now displays every paramter in the URL, including authenticity token of the #create form, all of the Job attributes, etc. Before the URL looked correct (/jobs/thanks) because the only params were action and controller which are already indicated in the URL.
Is there a way for me to use params.merge without displaying all of that info in the URL?
If you just want to pass the event as a parameter, you don't need to use params.merge at all.
redirect_to thanks_jobs_path(event: "reservation")
will give you the path /jobs/thanks?event=reservation.
You could simply use except:
params.merge.except(:auth_token)
In your situation:
redirect_to thanks_jobs_path(params.merge(event: "reservation").except(:auth_token))
I'm trying to follow the RailsTutorial guide, but by doing my own application instead. I have trouble with section 7, with the forms.
My controller :
def update
d = Deck.find(params[:id])
d.title = params[:deck][:title]
d.slug = params[:deck][:slug]
d.category = params[:deck][:category]
if d.save
redirect_to deck_path(d), notice: "Deck saved successfully"
else
render :edit
end
end
I know it's very, very far from good code, but I will refactor it later (if you have a suggestion I'm all ears, but I use Rails 3, so I guess the strong parameters of Rails 4 are out).
The problem is when the d.save does not work (due to validation), with the render :edit.
Right now, when I enter invalid data, it tries to redirect to the show action, and crashes because it does not have any data to display.
If I add #deck = d above the render, it works, but the url still is the show action.
If my validation fail, how can I stay on the same URL and display my error messages ? Is the "change URL but render the same page" behavior accepted as valid ?
Thanks !
If you're interested in looking at the rest of the code, it's here : https://github.com/cosmo0/TeachMTG/tree/remodel-decks
Actually when it fails your url is not the 'show' it is the 'update' url.
Your code works.
When you submit your form, your browser sends a POST request to controller#update.
When the update fails, you tell your update action to "render :edit". What it does is it renders the :edit action inside the :update route.
The update route uses the same url as your show action :
you can check this when running 'rake routes', the only difference is that the method is POST for 'update' vs GET for 'show'
That's why you think it is the show url in the browser but actually everything works: you are on the update action that renders :edit.
(Telling your update action to 'render :edit' doesn't mean you are redirected to :edit back from :update)
Is that clear enough ?
I believe that you're looking for respond_with method.
I think you are misunderstanding what is happening in the restful world. When you do an update the URL is changing because your form is performing a HTTP POST.
If d.save works it redirects to your deck_path with d as the object. If it fails it does not change the URL in the browser, but renders the same page as the edit action.
I'm guessing in the call for the edit action you have something like:
#deck = Deck.find(id)
Your render is failing because you don't have a #deck variable assigned in your update. So you can either change all of your instances of d to #deck or use your solution of setting #deck = d.
I have both task and request controllers and models respectively. The new action of request depends on a task :id which is included in the route. If the request cannot be save due to a validation error, I need to be able to render the new action of the request controller with the task :id. However, when the code below runs, I get a template is missing error.
How can I render the new template with the task :id as a param?
/task/1/request/new
if !#request.save
render :template => new_task_request_path(#blog) # /task/1/request/new
end
-----> Error: Template is missing -
Missing template blogs/1/requests/new
In your code, new_task_request_path(#blog) generates a relative URL.
When rendering a template you need to give it the path to the template, not the relative URL.
I can't tell where your view template is going to be without more information, but try changing your code in the create action to the following:
#task = Task.find(params[:id])
#request = #task.requests.new(params[:request])
if !#request.save
render 'new'
end
You can then access the Task ID using #task.id in your view.
This assumes that Task has many Requests, and that this action is in your RequestsController. If you're using a different relationship you'll need to update the controller action code as necessary. Similarly, if it's a different controller you should update the template path to be something like 'requests/new'.
I have a validation that needs to be done in controller. If it fails I need to go back to the view action back again with all values populated as it is on the page.
Is there a simple way to do that (using incoming params map).
This is the basic way all Rails controllers and scaffolds work. Perhaps you should try generating scaffolds?
def create
#banner_ad = BannerAd.new(params[:banner_ad])
if #banner_ad.save
flash[:notice] = 'BannerAd was successfully created.'
redirect_to :action => "show", :id => #banner_ad
else
render :action => "new"
end
end
end
I populate a #banner_ad here, attempt to save it, if it fails, I return to the form and the #banner_ad object is available to me. I then need to have a form that uses the Rails form helpers to populate the values from the object.
Depends on the flow of your app, really.
If the validation fails, you could pull the data out fo the database ...
if invalid?
#model = model.find(:id
end
Otherwise you might need to store the original values in hidden fields in the view and use those.