I have a standard controller which is set up to respond to HTML, JS and JSON requests:
def picture
#picture = Picture.new params[:picture]
respond_to do |format|
if #picture.save
format.html do
logger.debug "In the HTML responder"
redirect_to #picture
end
format.json { render json: #picture, status: :created, location: #picture }
format.js { render :nothing => true }
else
# you get the idea
end
end
end
Now I'm trying to send a request to that controller with the $.ajax function (I can't use :remote => true in this specific situation - I'm trying to make ajax file upload work).
$.ajax({
url: $("form#new_picture").attr("action"),
type: "POST",
data: formdata,
processData: false,
contentType: false
});
The problem is that my request is being treated as a HTML request for some reason. How do I tell rails that I want a JS response?
By the way, I'm using jquery_ujs in my project so I have access to the methods it provides if necessary. I'm not really good enough at JS to tweak that to do what I need here.
This solution didn't work for me (rails 3.1 + coffeescript). After searching quite a lot, I found the good way to do it and I wanted to share:
Just add ".js" to the end of the url. So simple... ;-)
Just add dataType: 'script'
$.ajax({
url: $("form#new_picture").attr("action"),
type: "POST",
data: formdata,
processData: false,
contentType: false,
dataType: 'script'
});
You have to set the 'accept' header before sending the ajax request so that Rails knows how to respond.
$.ajax({
url: $("form#new_picture").attr("action"),
type: "POST",
data: formdata,
processData: false,
contentType: false,
beforeSend: function(xhr, settings) {
xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
}
});
Add dataType: 'script' and in data of form add the parameter format: 'js' like this:
$.ajax({
url: '/manager/consumers/url',
type: 'GET',
dataType: 'script',
data: {
authenticity_token: '<%= form_authenticity_token %>',
param_1: '1',
param_2: '2',
format: 'js'
}
});
also add in the controller to not render layout:
respond_to do |format|
format.xls
format.js { render :layout => false }
end
Let me explain what is going on here.
I often get the Accept header and Content-type Header that a client sends to the server confused in HTTP. The Accept header is used to tell the server what content types (application/json, application/javascript, application/octet-stream, audio/mpeg, image/png, multipart/alternative, text/plain, text/html, text/csv, video/mpeg, etc) they'll accept. The server sends back a response, which includes the Content-Type header notifying the client of the actual Content Type of the content.
HTTP requests can also specify Content-Type, because in form data, there could be all types of data, and the Content-Type header can notify the server what the data actually is (e.g. multipart/form-data). The different media types like multipart/form-data are known as MIME.
Now jQuery.ajax() has another of parameters you can pass it related to this topic: accepts, contentType, dataType.
The contentType attribute is clear if you understand the Content-Type HTTP header. It tells the server what the data actually is. The default in jQuery is "application/x-www-form-urlencoded; charset=UTF-8", which is fine for most cases.
Remember that the Accept header tells the server what Content-Type it will accept. But when you read the jQuery documentation for dataType, it sounds quite similar: "The type of data that you're expecting back from the server." So what is the difference?
The accepts attribute allows you to change the Accept header in the request. But by changing the dataType it will change the Accept header too, so there is really no need to change the accept attribute; the dataType will change the Accept header. The benefit of dataType is ti allows you to pre-process the response before being available to the succes handler.
In effect, we need to tell Rails what we will accept as a response header, so we modify the dataType. In Rails the symbols :js and :json, for example, correspond to a HTTP Mime Type:
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
Thus, if we want to trigger the :js option in the respond_to block, then we need to specify the dataType in jQuery as script. And as one of the answers illustrates, you do it like this:
$.ajax({
url: "users/populate_user,
type: "POST",
data: formdata,
dataType: 'script'
});
Now look how beautiful the Request Header looks:
Notice how specifying the dataType as script changed the Accept header to application/javascript. Also notice that the contentType is "application/x-www-form-urlencoded; charset=UTF-8". Remember I said this is the default Content-Type jQuery will use if none is specified? Well another answer provided in this SO page specified this option as well:
contentType: false
According to the jQuery documentation:
As of jQuery 1.6 you can pass false to tell jQuery to not set any
content type header.
One last point. In your Rails controller, you do not need to specify the :js flag if this controller action is only ever going to be responding to :js. You can simply omit the respond_to from the controller:
def populate_user
#user = User.from_names(params[:name][:value]).first
end
And then add a users/populate_user.js.erb file. Also make sure your route is set up for a post request:
post 'users/populate_user', to: 'users#populate_user'
When copying and pasting answers from SO, it is also important to understand exactly what you are using in your project.
You can check whether it is a xhr request and render the format you prefer. For Example;
if request.xhr?
render :json => {
:some_data => 'bla'
}
end
Although possibly not directly answering the question, I came across this with a similar problem and others may find this helpful. Especially if you use haml.
After many attempts (including appending .js to the url) the only thing that worked for me was disabling the layout renderer when returning a script response.
respond_to do |format|
format.html
format.js { render layout: false }
end
My problem was that the AJAX response was a full html page generated by the rails layout template mechanism. This meant my javascript in my thing.js.erb wasn't executing. Disabling the layout meant only the javascript was returned (you can find the return in you browsers network tab of the developer pane) which allowed it to be executed.
I'm using haml as my default renderer and I believe that is why I required explicitly disabling the layout in js renders (I had assumed it would be automatic).
So if nothing else has worked and you use haml, try this.
Set contentType as "text/javascript"
$.ajax({
url: $("form#new_picture").attr("action"),
type: "POST",
data: formdata,
processData: false,
contentType: "text/javascript"
});
Related
I have an action that calls a javascript file which contains an ajax method like this one:
$.ajax({
type: 'POST',
url: "<%= some_action(model) %>",
dataType: 'json',
data: { 'something': true },
success: function(received_data) {
// Do something with received_data
$('#notice').html("<%= escape_javascript(render 'layouts/flash_messages', flash: flash).html_safe %>");
}
});
The "some_action" tries to put some info into flash[:success], and I want to get it in the success function so that I can pass it to the render.
I have already tried the flash.now[:sucess], but nothing. It seems that it is only possible to do this if I write in the flash hash from the action that calls this javascript file - but I don't want this since "some_action" will generate dynamic content.
Is that something possible to to?
Thanks for the help!
you can send js request instead of json request .
and then in your "some_action.js.haml" file you can write
$('#notice').html("<%= escape_javascript(render 'layouts/flash_messages', flash: flash).html_safe %>");
what's happening here is that your javascript file is not getting refreshed hence content of html is not changing .
I have:
<p id="click">Click here</p>
In contorller:
#details=Family.find_by(famid: params[:famid])
respond_to do |format|
format.html
format.json {render :json => #details}
end
and:
$('#click').on('click',function(){
$.ajax({
type: "GET",
data: 'famid='+id,
dataType: "json",
url: "/map",
success: function(data){
document.getElementById('myModal').innerHTML = data.famid
}
});
}
<div id="myModal"></div>
This works fine. The myModal div tag is populated by the correct famid value. But I just want to post the data to the controller via ajax and query the database and use the #details variable instead. So I tried:
$('#click').on('click',function(){
$.ajax({
type: "POST",
data: 'famid='+id,
dataType: "json", //do I need this? should it be html?
url: "/map"
});
}
Now I want to use the #details variable like:
<div id="#myModal"> <%= #details.famid %> </div>
How do I do that?
UPDATE: Ok I did the following things and everything works fine (thanks to #Rich & #NitinJ) except the partial. It's not rendered properly.
map.js.erb
$("#myModal").html("<%= escape_javascript(render(#details))%>");
_details.html.erb
<h3><%= #details.famid %></h3>
map.html.erb
<div id="myModal">
<%= render #details %>
</div>
Update:
Now the partial is rendered correctly in the browser console. I can see the #myModal div being populated correctly. But it's showing only in the browser console and not on map.html.erb page. What is wrong here?
Ajax
$('#click').on('click',function(){
$.ajax({
type: "POST",
data: {famid: id}, //you need to serialize your data
dataType: "json", //do I need this? should it be html?
url: "/map"
});
}
AJAX is quite simple - it sends a request on your behalf. The $.ajax function is from JQuery, so to get it right, you just need to pass the right arguments to it
Routes
#config/routes.rb
post "map", to: "your_controller#map"
Because you're sending a POST request (rather than GET), you'll need a route to handle the request. And since you're sending to /map, you need to ensure you're going to catch that request & send to the right controller
Controller
#app/controllers/your_controller.rb
def map
#details = Family.find_by(famid: params[:famid])
respond_to do |format|
format.html
format.json {render :json => #details}
end
end
Response
Because you're dealing with JSON, I believe you'll be best handling the response in the view directly, like this:
$('#click').on('click',function(){
$.ajax({
type: "POST",
data: {famid: id}, //you need to serialize your data
dataType: "json", //do I need this? should it be html?
url: "/map",
success: function(data) {
details = jQuery.parseJSON(data)
$('#modal_body').html(details.famid);
}
});
}
To my knowledge, JSON is meant to pass data succinctly (with as few moving parts as possible), and consequently, we always handle the JSON response from the Ajax request; rather than a separate file
You can use the jQuery.parseJSON function to handle this, creating an object you can then append to the page
Update
I believe your problem is you're using JSON
You can't load a rails .js file with JSON - you have to load .json.erb, or process in the front-end view. You may wish to change your request to standard JS:
#JS
$('#click').on('click',function(){
$.ajax({
type: "POST",
data: {famid: id},
url: "/map"
});
}
#Controller
def map
#details = Family.find_by(famid: params[:famid])
respond_to do |format|
format.js
format.html
end
end
#app/views/controller/map.js.erb
$("#myModal").html("<%=j render(#details) %>");
You have to create a js.erb template than render this template. Now you can access this variable in your *.js.erb template
alert("<%=#details.famid%>")
you can also update your text of p tag by
$("modal-body").text("<%=#details.famid%>")
I have a RESTful controller:
class Api::V1::DevicesController < ApplicationController
before_filter :require_user
respond_to :json
# PUT /api/v1/devices/1
def update
#device = Device.find(params[:id])
authorize! :update, #device
#device.update_attributes(params[:device])
respond_with #device
end
end
And some JS on the client side:
$("#click-me").on('click', function() {
$.ajax({
type: "PUT",
url: '/api/v1/devices/' + $(this).data('device-id'),
dataType: 'json',
accept: 'application/json',
data: {device: {user_id: null}, format: 'json'},
success: function () {
alert("Booya!");
}
})
});
When the AJAX gets fired, by default, jQuery sends a JS object as a URL encoded string.
And a Content-Type:application/x-www-form-urlencoded which is fine. And because I set dataType to 'json', it sets Accept:application/json, text/javascript, */*; q=0.01, which also seems fine.
However when Rails gets the request, it treats the post body as JSON, even tho content-type is URL Encoded. Is this a bug?
My solution so far is to cast the JS object as JSON (JSON.stringify({device: {user_id: null}, format: 'json'})) and just submit it.
But that doesn't feel very good, surely Rails should handle Content-Type and Accept separately. This app is in 3.0.17. Perhaps it's an issue with earlier versions?
UPDATE:
Turns out the 422 I was receiving was a validation failure, and Rack is smart enough to deal with varying Content-Type and Accept headers. Duh.
This is maybe due to the
dataType: 'json',
line, which informs the server that the content of your request is JSON.
I have a problem when sending the Ajax request. When you click on the link request is sent to the server 3 times and the answer did not come.
Why the request is sent three times to undermine?
Where did I go wrong in the formation of a query?
code:
run.html.erb
...
<%= link_to "Next", "#", :id => 'next', :class =>
...
run.js.erb
(function(){
$("#next").click(function(){
$.ajax({
type: 'POST',
url: '/engine/tff',
success: function(data){
alert("ok");
$("#question").html(data);
}
});
return false;
});
});
controller
def tff
respond_to do |format|
format.js render :text => "hello"
end
end
I am guessing the click event is being bound multiple times probably cos the script is being called multiple times. Not sure what is the reason for the multiple calls as I am not familiar with rails.
what you could do to avoid it is unbind the click event before binding it or use the on api.
function ajaxRequest(){
$.ajax({
type: 'POST',
url: '/engine/tff',
success: function(data){
alert("ok");
$("#question").html(data);
}
});
return false;
}
$("#next").unbind("click").bind("click",ajaxRequest);
or
$(document).on("click","#next",ajaxRequest);
also not sure if you were trying to bind on document ready and there is a typo in your code. but it should be wrapped like this:
$(function(){
$("#next").click(ajaxRequest);
});
One thing I ran into recently was that jquery tries to run the result, as the default dataType interprets the .js as a script, not text. You might need to add a dataType: "text" to the ajax call. I don't see how this translates into three calls though.
I thought returning false was supposed to prevent the click from moving on, but perhaps it is better form to use preventDefault() as apneadiving suggests.
I'm sending a request from jquery and expecting json response. Configuration is like this:
type: 'POST',
cache: false,
iframe: true,
dataType: "json",
Now there's a funny thing. When I return data as text, it works fine:
render :text => "{}"
But when I use :json option, firefox prompts me to download file containing exactly two characters, {}. Success callback is never invoked.
render :json => {}
Since there's already a workaround (see above), the question has only theoretical value. Did it ever happen to you?
It's probably due to that you are setting iframe to true, so firefox shows the download prompt, either set
iframe: false,
otherwise you have to make rails send the text/plain header by using
render :text => "{}"
Regards.