I'm trying to render a partial from a js.erb file but nothing is shown in the view.
There is an AJAX call that triggers the report_announcement action in the announcements_controller when a button is pressed.
reports.js
$.ajax({
type: "POST",
contentType: "application/json",
url: "/announcements/report_announcement.js",
data: json_data
});
reports/_report_content.html.erb
<div id="report_announcements_container"></div>
announcements_controller.rb
def report_announcement
#announcement = Announcement.new(announcement_params)
respond_to do |format|
if #announcement.save
format.js {}
end
end
end
announcements/report_announcement.js.erb
I know that announcements/report_announcement.js.erb is rendering ok because I can see the logging statement in the console and also in the development.log as well as the report_announcement object being logged to the console.
<% announcement_json = #announcement.to_json.html_safe %>
var report_announcement = <%= announcement_json %>;
console.log('report_announcement');
console.log(report_announcement);
// this is where something is not right I think
$("#report_announcements_container").html("<%= escape_javascript(render partial: 'announcements/report_announcements', locals: { announcement: #announcement }) %>");
console.log('inside announcements....');
announcements/report_announcements.html.erb
This is where I'm having an issue because the logging statement for the partial is shown however nothing from the partial is shown on the page. I see in the development.log that the page rendered but nothing is actually shown in the view. The partial is not showing on the page in the report_announcements_container div.
<h1>test</h1>
<%= announcement %>
<script>
console.log('inside partial...');
</script>
You're using the wrong content-type in your ajax call. Rails recognizes both application/javascript and text/javascript as the :js format.
Also when you request a js format you actually have to execute the returned javascript for anything to happen in the client.
$.ajax({
type: "POST",
contentType: "application/javascript",
url: "/announcements/report_announcement.js",
data: json_data
}).done(function(data){
jQuery.globalEval(data); // Yeah eval is evil.
});
If you have done any "real" ajax calls before this will seem completely backwards - thats because it is. js.erb is a poor mans ajax framework for the javascript impaired which consists of a bunch of routes and views that mutate the DOM in the client.
Manually creating calls for a js.erb template is just a silly excerise in masocism. If you really want to use it create a form and use it send the request instead so that you can just leverage the javascript handlers that rails provide:
<%= form_with(url: "/announcements/report_announcement", remote: true) do |f| %>
# ...
<% end %>
In my Rails app, stringified JSON form input is passed to a controller via AJAX - on success, the user is to be redirected to a summary page, but the AJAX redirect doesn't seem to be working...
$.ajax({
url: "report/submission",
type: "POST",
beforeSend: function(xhr) {xhr.setRequestHeader("X-CSRF-Token", $("meta[name='csrf-token']").attr("content"))},
data: {"report" : reportParameter},
success: function(response) {
window.location.href = "/report/summary";
}
});
and the associated controller
def submission
#incomingReport = ActiveSupport::JSON.decode(params[:report])
#newReportIDArray = Array.new
#incomingReport.each do |x|
hash = ActionController::Parameters.new(x)
#new_report = Report.new(report_params(hash))
#new_report.save
end
end
Everything else seems to work just fine - the data is entered, but the redirect does not trigger. I've searched all around and it looks like this is the syntax that everyone says to use, but it doesn't seem to work for me. I'm sure that I am doing something wrong, but I'm not sure what.
Editing to clarify problem/solution
During a chat with #Jonathan and #Kumar, I noted that window.open("/report/summary") did work correctly - #Jonathan suggested that I just try console.log(window.location) before the ajax call, and to my surprise, the script from a function from elsewhere in my app was logged. Big shocker now - THE FUNCTION WAS CALLED location()!!! Renaming the function and then restarting the app in a new window solved the problem. Learn from my mistake, kids - don't name a function location().
Ruby isn't my first language but it doesn't look like you're sending a response back. Try returning something or putsing. Look up how to do that with rails, a proper response. Maybe render json: [success: 200] or something like that. Maybe it's irrelevant. In any case, if it's not working try changing success for complete and log out the response to debug. The complete will always fire, but success won't always.
Try this:
respond_to do |format|
format.json do
render json: {
success: 200
}.to_json
end
end
In your AJAX setup, add "datatype": "json".
You could improve the response to conditionally send a failure like success: 500 if something went wrong.
You don't really need respond_to block here because you're always expecting JSON, but that's the kind of format that's often used in Rails if not mistaken.
If that doesn't work just use the render json: part as that is definitely a return.
Update
Further from our discussion it turns out that after making a robust Ajax call and tweaking the action, the final hurdle was a window.location that was not working. The cause of the problem was that location had been rebound to another function. All that needed to be done in the end is to rename that custom function and Bob's your uncle.
Add a datatype
$.ajax({
url: "report/submission",
type: "POST",
dataType: 'json', #Add json data type, as we'll render json from controller
beforeSend: function(xhr) {xhr.setRequestHeader("X-CSRF-Token", $("meta[name='csrf-token']").attr("content"))},
data: {"report" : reportParameter},
success: function(response) {
console.log("Response is ", response);
//When we get 200, this function should execute
window.location.href = "/report/summary";
},
error: function(error){
console.log("Error is ", error);
}
});
And in the controller
def submission
#incomingReport = ActiveSupport::JSON.decode(params[:report])
#newReportIDArray = Array.new
#incomingReport.each do |x|
hash = ActionController::Parameters.new(x)
#new_report = Report.new(report_params(hash))
#new_report.save
end
respond_to do |format|
format.json { head :ok } #This will return 200 status back to ajax call
end
end
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 .
Background and my research
I would like to send data from server to client without reloading the page on the browser. I was reading Rails guide and using JSON seemed promising. (Correct me if I am wrong).
2.2.8 Rendering JSON
JSON is a JavaScript data format used by many AJAX libraries. Rails
has built-in support for converting objects to JSON and rendering that
JSON back to the browser:
render :json => #product You don’t need to call to_json on the object
that you want to render. If you use the :json option, render will
automatically call to_json for you.
Problem
I want to be able to render this JSON object back in the browser side. The following are appropriate code for the problem.
VideosController
class VideosController < ApplicationController
include VideosHelper
def home
#video = Video.last
end
def next
#next_video = next_video(params[:id])
render :json => #next_video
end
end
next.json.erb
{
"title": "<%= #video.title %>"
"url": "<%= #video.url %>"
"youtube_id": "<%= #video.youtube_id %>"
"genre": "<%= #video.genre %>"
"category": "<%= #video.category %>"
"likes": "<%= #video.likes %>"
"dislikes": "<%= #video.dislikes %>"
"views": "<%= #video.views %>"
"user_id": "<%= #video.user_id %>"
"created_at": "<%= #video.created_at %>"
"updated_at": "<%= #video.updated_at %>"
}
If I want to render #next_video JSON object on home action. How do I do this?
You need some js handler i guess. Something like that in home.html.erb:
$.ajax({
url: '<%= next_video_path(#video.id) %>',
type: "GET",
dataType: "json",
success: function(data) {
// something brilliant here
}
})
also in next.json.erb you should change #video to #next_video(since you don't have #video initialized in next action). Also one more suggestion - you can use REST those renaming next to show and deleting home(since you can replace it with show now) and you will need to change previous code to:
$.ajax({
url: '<%= video_path(#video.next) %>',
type: "GET",
dataType: "json",
success: function(data) {
// something brilliant here
}
})
Where #video.next will return id of the next video.
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"
});