I have a controller action: create which is responding to json format and html.
respond_to do |format|
if #pr.save
format.html {redirect_to(some_path)}
format.json {render json: #pr, status: :ok}
else
format.html {}
format.json {}
end
end
So from a angular service I send a post request to this action, if it is successful I would like to redirect the user to some_path in my server log I see that redirect bing made, but the page is not changing.
How can I fix this? I am using turbo links as well so I would like not loading all the files again.
If you are using Turbolinks you can do:
Set a location in your controller:
format.json {render json: #pr, status: :ok, location: some_path}
Get the location from your header response in js:
header('Location')
Use Turbolinks visit:
Turbolinks.visit(header('Location'));
And now you are not loading the assets.
You should have a route where you want to redirect your user. Then In controller You can call the page
if( <Your Condition true/false> )
$location.path('/dashboard');
remember you should have added dependency $location in your controller
Or you can take a look here :
How to integrate Rails views with angularjs ng-view?
Related
I want to redirect to another page admin_antenna_reader_rfids_path at the end of the create method. I did:
def create
#antenna_reader_rfid = AntennaReaderRfid.new(antenna_reader_rfid_params)
if #antenna_reader_rfid.save
render json: {status: true}
redirect_to admin_antenna_reader_rfid_path(q#antenna_reader_rfid)
else
render json: {errors: #antenna_reader_rfid.errors.full_messages, status: false}
end
end
I get an error AbstractController :: DoubleRenderError:
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
How can I solve this?
You have to remove the line render json: {status: true} as currently you're trying to make your controller render a json and redirect to an HTML page at the same time. You have to pick one.
To handle multiple request format, you can use respond_to
if #antenna_reader_rfid.save
respond_to do |format|
format.json { render json: { status: true } }
format.html { redirect_to where_you_want_path }
end
else
# same way as above
end
Within the respond_to block, you can render all the request formats as you want, then based on the request header, the controller will choose the corresponding logic to respond to you.
You can't render nor return more than once in a method.
In a standard, scaffolded Rails 5.1 (or 5.0) controller, you get this on the create action:
def create
#test = Test.new(test_params)
respond_to do |format|
if #test.save
format.html { redirect_to #test, notice: 'Test was successfully created.' }
format.json { render :show, status: :created, location: #test }
else
format.html { render :new }
format.json { render json: #test.errors, status: :unprocessable_entity }
end
end
end
As you see, there is no format.js there.
But if you add remote: true to the form (or, in Rails 5.1, you remove the local: true which will use the new default which is to post via ajax), the form works: the browser will send a post via xhr and redirect to the newly created record.
Looking at dev tools, I see that the response for the form submission was a 200 OK with the following content:
Turbolinks.clearCache()
Turbolinks.visit("http://localhost:3000/tests/10", {"action":"replace"})
Console also indicates it was processed by Javascript:
Started POST "/tests" for 127.0.0.1 at 2017-06-18 09:38:25 -0300
Processing by TestsController#create as JS
The question is then: how is Rails handling this response/redirect for the JS request if there's no format.js in the controller?
I'm all in for Rails magic but I want to know how this works, and I haven't seen this 'fallback the JS request to the format.html block' documented anywhere.
It looks like the code that is generating that response comes from the turbolinks-rails gem.
https://github.com/turbolinks/turbolinks-rails/blob/v5.0.1/lib/turbolinks/redirection.rb#L14
From the linked code it looks like turbolinks prepares a js response when redirect_to is called, the request is XHR, not a GET request, and turbolinks: false was not provided to the redirect_to call.
Turbolinks overrides ActionController redirect_to when the gem is present and app.config.turbolinks.auto_include is truthy.
def redirect_to(url = {}, options = {})
turbolinks = options.delete(:turbolinks)
super.tap do
if turbolinks != false && request.xhr? && !request.get?
visit_location_with_turbolinks(location, turbolinks)
else
if request.headers["Turbolinks-Referrer"]
store_turbolinks_location_in_session(location)
end
end
end
end
This behavior is intended and implemented by ActionView::LookupContext:
https://github.com/rails/rails/blob/master/actionview/lib/action_view/lookup_context.rb#L251
# Override formats= to expand ["*/*"] values and automatically
# add :html as fallback to :js.
def formats=(values)
if values
values.concat(default_formats) if values.delete "*/*".freeze
if values == [:js]
values << :html
#html_fallback_for_js = true
end
end
super(values)
end
There is an open PR to change this behavior, but it's stalling now:
https://github.com/rails/rails/pull/15224
There was some discussion about this other PR:
https://github.com/rails/rails/pull/5892
Rails scaffold generated the following:
respond_to do |format|
if #student.save
format.html { redirect_to #student, notice => 'Student was successfully created.' }
format.json { render :show, status: :created, location: #student }
else
format.html { render :new }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
After reading this I understand how the respond_to is working (sort of), but I don't get what format is doing. Shouldn't it be either format.html or format.json and not both? What are these two lines actually doing?
format.html { render :new }
format.json { render json: #student.errors, status: :unprocessable_entity }
Is there an implied if in there? Is it something like
if (format == html) {}
if (format == json) {}
Side note: Why does update require the respond_to block while show will handle /students/1.json or /students/1 without any logic at all?
format is a local variable that respond_to yields. When you do format.html {} you are actually registering a callback block for a format.
Rails goes through the registered formats and tries to find a compatible format to the MIME type in the request. If there is no handler it will raise an error.
This could be explained as something like using syntactic sugar on top of a case statement (the Ruby equivalent of a switch statement). But the analogy is not completely accurate since Rails does a bit of work in matching the request type.
Also the code inside your block is not executed when the format.html block is registered (as it would be if it was just a conditional statement) but rather when respond_to finishes or not at all if you are using for example E-Tag caching.
Why does update require the respond_to block while show will handle
/students/1.json or /students/1 without any logic at all?
Rails handles many actions by using a convention over configuration approach and guessing the intent of the action.
def PostsController < ApplicationController
def index
# rails auto-magically fills in the controller with something
# like this
#posts = Post.all
respond_to do |format|
format.html { render :index }
format.json { render json: #posts }
end
end
def show
# convention over configuration is awesome!
#post = Post.find(params[:id])
respond_to do |format|
format.html { render :show }
format.json { render json: #post }
end
end
def new
#post = Post.new
render :new
end
def edit
#post = Post.find(params[:id])
render :edit
end
end
Rails assumes that there is a resource with the same name as the controller and auto-magically fills in the controller action. It also assumes there is a view in app/views/posts/(:action).html.[erb|haml|slim|jbuilder]. This is known as implicit rendering.
The comments show roughly what action rails attempts.
It does not fill in actions which operate on data (create, update, destroy) since the actual implementation can vary greatly and it's hard to make useful guesses.
Well, it depends on the format of the request. If a request demands HTML from the server, format.html block will be executed, and in the same way, if a request demands JSON format, format.json will be executed.
Rails will automatically(read: magically) handle the if (format == html) part for you. All you have to do is fill in the blanks. Same way, you can write a block for XML starting with format.xml.
And for the side note, I think you have said it otherwise. update method doesn't require respond_to block, while show requires. And the reason is very simple: update method is there to update the Model, and then, redirect you to somewhere, while show will always return you something. In your case, /students/1 will return you the first student created in the database, and the response will be HTML, while /students/1.json will return you the same result, but response will be JSON this time.
Well you could very well replace 'format' with 'foo' or 'banana' or whatever you want. It is just the variable name in this case because the variable that is sent to your block by respond_to is passing along the format as requested by the incoming http request's Accept header.
Sometimes you'll see 422 "Unacceptable" errors in your logs because you are receiving a request with an Accept header that does not request a mime type your app knows about.
As it is, your callers should be using a browser or be a JSON consumer sending the proper headers to receive responses from the boilerplate.
I am trying to go back to the previous page after updating a link. Here is my link controller:
def update
if #link.update(link_params)
redirect_to :back
else
format.html { render :edit }
format.json { render json: #link.errors, status: :unprocessable_entity }
end
end
Although the link does update, it the page only refreshes, instead of going back to the previous page. Could someone help point out what I am doing wrong? Thanks!
Referrer
Each time you load a controller action in your application, you'll get a request object, which should have the referer attribute:
def update
if #link.update(link_params)
redirect_to request.referer
else
format.html { render :edit }
format.json { render json: #link.errors, status: :unprocessable_entity }
end
end
The problem is that you are sending a request from your browser in a way that it wants to handle the response as if you were going to a new page. redirect_to :back simply tells rails to send the referrer as the redirect URL. If the code read redirect_to 'http://google.com' you would expect the browser to go to google.com, would you not? The correct thing to is to make an asynchronous call using javascript and use javascript to go back in the event of success.
How this happens depends on which JavaScript library you are using. Simply make the call, and in your success function, call window.history.back() and the browser will go back.
I took over someone else's Rails project and I have a question about HTTP requests.
It SEEMS that I should be able to pass parameters through HTTP requests, I'm just unsure how. For example: rake routes shows
PUT /auction2s/:id(.:format) auction2s#update
Which seems to correspond to this function
# PUT /auction2s/1
# PUT /auction2s/1.json
def update
#auction2 = Auction2.find(params[:id])
print "Hello World"
respond_to do |format|
if #auction2.update_attributes(params[:auction2])
format.html { redirect_to #auction2, notice: 'Auction2 was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #auction2.errors, status: :unprocessable_entity }
end
end
end
But I can't figure out the URL I would need to pass to, for instance, change
id=18445&done=true
into that function.
Any thoughts? Is the function structured right? Do I just need to pass the request in a Ruby format, not through the browser or AJAX (which is what I'm trying)?
You should have a form for this action. Most likely in this location -> app/views/auction1s/edit.html.erb. It will be edit.html.haml if you are using haml template engine. The form will be rendered in the view and user input will be sent as parameters to this action on submit of the form.