Reputation: 5918
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.
Upvotes: 3
Views: 1007
Reputation: 928
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
Upvotes: 0
Reputation: 3133
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
Upvotes: 1