I have a javascript file with some ajax in it as follows.
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
url: 'data',
dataType: 'json',
success: function (data) {
draw(data);
},
error: function (result) {
error();
}
});
I also have created a method in a controller which renders json.
class GraphController < ApplicationController
def index
end
def data
render :json => User.select('value').map(&:value)
end
end
So my question is, how does rails know where the json is coming from especially as I'm not returning a physical file from my controller. What happens if I also have a physical .json file in my folder structure? Is there a heirarchy of how a view will look for a json file (eg physical file>jbuilder file>controller action)?
Every Action must render something.
Either you render something explicitly or rails will look for a page with name similar to the action in inside respective controller's folder in views.
You may render an html.erb, .json, .haml etc from views( provided you specify respond to format)
If you are rendering someting explicitly (which is true in your case, as json) Rails wont bother to look into views folder.
Or otherwise you may just skip render :json statement and specify that object in .json file, with respond to :json.
Here in your scenario, you are rendering a json object, which will be accepted in the success: section of the AJAX function's data arguement.
in ajax call.. contentType: "application/json; charset=utf-8", defines what is the type of request you are querying with rails.What that says is, "if the client wants HTML in response to this action, just respond as we would have before, but if the client wants XML, return them the list of people in XML format." (Rails determines the desired response format from the HTTP Accept header submitted by the client.).
Take a look respond_to api how rails responds to different types of request -js/xml/html/json
so you can try this in your controller as well..edit the data action like this and try..any call to data such as js/html/xml/json will work and rails will understand what type of response it needs to send.
def data
format.html { redirect_to(user_list_url) }
format.js
format.xml { render :xml => #users.to_xml(:include => #accounts) }
format.json {render :json => User.select('value').map(&:value) }
end
to render any error from controller to view..can be done like this:-
if #user.present?
format.json {render :json => User.select('value').map(&:value) }
else
format.js { render :json => #user.errors.full_messages.join(' '),
:status => 400 }
end
use this in view in ajax.error function like this
.error(function(data) {
$("#show_error").html("An Error Occurred,Please try again.");
});
HOPE THIS HELPS
In my rails app I send request in html, ajax, xml and json. For xml and json I do it by writing :format => 'json/xml', for ajax we write :remote => true.
But how server know that it has to respond with ajax, xml or json?
One thing I noticed when I send json or xml request it show in the url like below
http://localhost:3000/en/line_items.json
Though Im not sure, this is how server know if he has to give json response. But for html and ajax something like this is don't add in the url. So how server know whether it is ajax or html request?
Request headers are governed by mime-types, which basically determine which type of content / request is being sent
Rails has an in-built handler to manage different responses. As per #hawk's URL on ActionDispatch::Request, you can see this middleware essentially catches the type of request sent from the browser
Respond_To
The real answer to your question is about the respond_to block:
#apps/controller/your_controller.rb
def new
respond_to do |format|
format.html { # do stuff here }
format.js { # do stuff here }
format.json { # do stuff here }
end
end
This will allow you to send various responses depending on the mime-type sent in the request :)
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"
});
I'm running the latest Rails 2-3-stable branch (currently 2.3.3).
I'm using JQuery to post an AJAX request to my 'create' action, in which I have the following block:
respond_to do |format|
format.js
end
I have created create.js.erb and to test this action, I've added the following single line:
alert('hello');
The request correctly enters the format.js block, but the response attempts to render a layout. Here's my log:
Jul 22 20:44:27 [2970] INFO: Rendering template within layouts/application
Jul 22 20:44:27 [2970] INFO: Rendering contacts/create
If I change my respond_to block to the following, it works:
respond_to do |format|
format.js { render :layout => false }
end
Is this expected behaviour or is this a bug in Rails? I would have thought the fact that I'm rendering a JS response would be enough to set layout to false.
I use this:
class ApplicationController < ActionController::Base
# this is needed to prevent XHR request form using layouts
before_filter proc { |controller| (controller.action_has_layout = false) if controller.request.xhr? }
It works like charm. You have to put this onliner only in one place and noting more.
I have spend ~1 h to write this line.
The best way I've found to handle this is to add a file like this:
app/views/layouts/application.js.erb
<%= yield -%>
If you don't include the yield, your js actions will all fail, since the app will never render the view. This technique solves the layout problem and also offers the ability to add universal js code at the layout level, such as flash processing or triggering jQuery UI onLoad hooks.
My memory of Ajax on Rails books is that this was the standard if not necessarily expected behaviour in earlier editions of rails.
The following ticket shows the bug logged a few versions back, as well as a way to define the behaviour as default.
http://dev.rubyonrails.org/ticket/3229
Sadly the description of what the later changes are that make this obsolete are not explained in the final comment.
The JQuery function which makes the Ajax POST request should return "false". Check if you are doing the same. It should be implemented like following:
postFormData: function () {
$('#form_id').submit(function () {
$.post($(this).attr('action'), $(this).serialize(), null, 'script');
return false;
});
}
Notice the last line which returns false.
I was putting together a quick inline editing feature in my first Rails app and just as I was getting it working it occurred to me that I may be violating RESTful principles. The edit updated an image name. To do so, it submits, via PUT to Image#update and passes the new modified name as image[name].
The database gets updated properly, but I need that value back so that my markup can reflect the name change. To do that, I was calling /images/:id.json, but that got me wondering whether a PUT request can "validly" (in that RESTful sort of way) return a value like this.
Thoughts?
Update: For whatever it's worth, I'm using jQuery and the jEditable plugin to do the inline editing. Here's my jEditable code:
$(document).ready( function() {
$('h2').editable(
'/images/' + $('#image-id').val() + '.json',
{
method: 'PUT',
name: 'image[name]',
submitdata: { authenticity_token: $('#auth-token').val() },
submit: 'Save',
cancel: 'Cancel'
}
);
})
And my Image#update method as it exists right now:
def update
#image = Image.find( params[:id] )
if #image.update_attributes( params[:image] )
flash[:notice] = "Successfully updated image."
respond_to do |format|
format.html { redirect_to #image }
format.json { render :json => #image.to_json }
end
else
render :action => 'edit'
end
end
If your concern is just that your update method with JSON provide a response body and not just a 200 OK (Rails's head :ok) then I don't think you need to be worried. The default response is 200 OK so the only difference between what you're doing and what Rails does by default (in its scaffolds) is that you're also including a response body. As far as I can tell proper REST etiquette only requires that you return a 200 OK and doesn't care about the response body, which is in line with what you're doing.
Beyond that all your code looks excellent.