rails respond_to format.js API
respond_to do |format|
format.js # actually means: if the client ask for js -> return file.js
end
js
here specifies a mime-type that the controller method would send back as a response;
Default Rails mime-types.
If you try also with format.yaml
:
respond_to do |format|
format.js
format.yaml
end
that will mean that your controller will return yml
or js
depending on what the client-side is asking;
{}
in terms of ruby is a block;
If you don't specify any rails will try to render a default file from app/views/[contoller name]/[controller method name].[html/js/...]
# app/controllers/some_controller.rb
def hello
respond_to do |format|
format.js
end
end
will look for /app/views/some/hello.js.erb
; // at least in Rails v. 2.3.
If you do specify block:
respond_to do |format|
# that will mean to send a javascript code to client-side;
format.js { render
# raw javascript to be executed on client-side
"alert('Hello Rails');",
# send HTTP response code on header
:status => 404, # page not found
# load /app/views/your-controller/different_action.js.erb
:action => "different_action",
# send json file with @line_item variable as json
:json => @line_item,
:file => filename,
:text => "OK",
# the :location option to set the HTTP Location header
:location => path_to_controller_method_url(argument)
}
end
respond_to do |format| format.js not working in rails 6.1.3
I think you are missing remote: true
= form_for @contact , url: contacts_path, remote: true do |f|
From the documentation:
:remote - if set to true, will allow Unobtrusive Javascript drivers to
control the submit behavior
This is what adds the js header to the request and allows the rails controller to respond with the js format.
format.js and format.json in same respond_to
Rails determines the format to take, by reading the Accept
header of the http request:
Example:
def test
f.js {
render js: "Hello World"
}
f.json {
render json: { foo: :bar }
}
end
curl localhost:3000/test -H 'Accept: application/json'
{"foo":"bar"}%
curl localhost:3000/test -H 'Accept: application/javascript'
Hello World%
Try changing the header accept
in your XHR call, e.g. when using fetch
:
fetch(url, {
headers: { 'Accept': 'application/json' },
})
Similar in jquery's.ajax or raw XHtmlRequest.
Update Rails Content negotiation:
Rails has a heuristic for determine the format. Check the Doc: https://api.rubyonrails.org/classes/ActionDispatch/Http/MimeNegotiation.html#method-i-formats
formats.first
will be taken as the "format" for the request, and the order is like this. First rule will "win":
- a parameter called "format", e.g. ?format=json
- Accept header
- path suffix, e.g.
/foo/bar.json
- When XHR -> Always js
- otherwise HTML
Rails, respond_to do |format| returns ActionController::UnknownFormat
The most likely explanation is that the request is not requesting JS. In order for rails to treat the request as JS the path needs to end with .js
or have the correct Accept
or Content-Type
headers. Not that the mime type is not registered with rails as it registers the most common mime-types for javascript.
- Check the rails log to confirm:
tail -f logs/development.log
- Check the browser console for errors.
- Check that form that is has the
data-remote="true"
attribute.
<%= form_for(@thing, remote: true) %>
How to respond_to js.erb?
I found an answer to my question here.
I solved the issue with a line of code in my controller respond format.js by adding this line: {render layout: false}
Another solution might be adding data: {type: "script"}
to your form_for, but in my case it didn't work.
The working controller looks like this:
def create
@feedback = Feedback.new(feedback_params)
respond_to do |format|
@feedback.save
format.html {}
format.js {render layout: false}
end
end
Thanks everyone for being involved.
using respond_to do |format| format.js to show a message inside of a modal after photo is favorited inside a modal?
To utilize the JavaScript response, you will need to add the remote: true
option to your link.
http://guides.rubyonrails.org/working_with_javascript_in_rails.html
If you want to handle this via AJAX, don't use redirect back.
Also use flash.now
for AJAX.
Instead handle both scenarios in you're JavaScript file.
# app/views/photos/favorite.js.erb
<% if flash.now[:notice] %>
$('.modal-dialog.photo>.message').html("<%= j flash.now[:notice] %>")
<% else %>
$('.modal-dialog.photo>.message').html("<%= j flash.now[:alert] %>")
<% end %>
# app/views/photo/unfavorite.js.erb
<% if flash.now[:notice] %>
$('.modal-dialog.photo>.message').html("<%= j flash.now[:notice] %>")
<% end %>
It kind of goes that notice
is like success and alert
are error.
And change your controller to this:
def favorite
@photo = Photo.find params[:id]
favorite_photo = current_user.favorites.build(photo: @photo)
if favorite_photo.save
flash.now[:notice] = 'You successfully favorited this photo'
else
flash.now[:alert] = favorite_photo.errors.full_messages
end
respond_to :js
end
def unfavorite
@photo = Photo.find params[:id]
current_user.favorites.delete(@photo)
flash.now[:notice] = 'You successfully unfavorited this photo'
respond_to :js
end
And your routes to match this configuration.
I'd POST
to favorite and DELETE
to unfavorite due to the DB implications of each.
Currently you can only pass notice
and alert
to a render
or redirect_to
. But you can defined your own flash types. But don't constrain yourself by using only these two.
if success
flash[:success] = "Something good"
else
flash[:error] = "Something bad"
end
Why is respond_to executing format.html instead of format.json when using AJAX?
The way I understand respond_to bock is that when you make an AJAX
request it should answer back by using json, and If you make a regular
request it should answer back by using html. Is that correct?
Not quite. An AJAX request is just an asynchronous request and the response type should depend on the Content-Type and Accept-Type headers. An AJAX request request can actually request any possible content type - JSON is just the most commonly used and arguably the most useful type.
If the request does not contain a specific content type or accept type Rails will default to html unless you override it in the routes:
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :things
end
end
Rails UJS which is built into rails and powers the remote: true
option on forms and links actually uses application/javascript
as the default content-type as it lets you write js.erb
views and reuse rails templating without writing ajax handlers. If this is really a good idea though is debatable as it leads to some very questionable design decisions.
With Rails UJS the easiest way to set the content type is through the data-type
attribute:
<%= link_to "Click Me!", "/foo", remote: true, data: { type: :json } %>
<%= form_with(model, html: { data: { type: "json" }}) %>
If you are sending an Ajax request "manually" with XMLHttpRequest you can set the content type with setRequestHeader
.
xhr.setRequestHeader("Content-Type", "application/json");
With jQuery you use the type:
option for the ajax funtions or jQuery.getJSON
.
Also the correct way to respond is to a successful POST request is with 201 Created.
201 Created. The request has been fulfilled and has resulted in one or
more new resources being created. The primary resource created by the
request is identified by either a Location header field in the
response or, if no Location field is received, by the effective
request URI.
format.json { head :created, location: @role }
You can also optionally include the created resource in the response body.
format.json { render json: @role, location: @role, status: :created }
Rails 3.1: The new way to use 'respond_to ... format.js' without the 'page' variable
I'd go with:
render js: %(window.location.href='#{article_path @article}')
Or put something along the lines of this in your application_controller.rb
:
def js_redirect_to(path)
render js: %(window.location.href='#{path}') and return
end
Then in your controllers you can just call:
js_redirect_to(article_path @article)
Note I just made this up - not tested :)
Related Topics
Serving Static Files With Sinatra
Pass a Variable into a Partial, Rails 3
How to Access Method Arguments in Ruby
Installing Nokogiri on Osx 10.10 Yosemite
How to Create a Class Instance from a String Name in Ruby
In Ruby, How Does Coerce() Actually Work
Using Rails Migration on Different Database Than Standard "Production" or "Development"
Why Do I Get a "Permission Denied" Error While Installing a Gem
How to Search a Folder and All of Its Subfolders For Files of a Certain Type
How to Replace a Hash Key with Another Key
Equivalent of .Try() for a Hash to Avoid "Undefined Method" Errors on Nil
Is There a Better Way of Checking Nil or Length == 0 of a String in Ruby
Get Index of Array Element Faster Than O(N)