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.
Related
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 %>
I'm using the gem gem 'axlsx_rails' and I want to export data to an excel File but I need to send data to the controller via AJAX, like checkboxes clickd in js or items selected. The problem is that with ajax the data is sent to controller but the xlsx file is not downloading but using the rails Link-to function works good.
This is my function in controller. It sents a variable to the xlsx file that is rendering just the name attribute for user. Not big deal.
def export_xlsx_report_table
enterprise = current_user.enterprise
p "Params!!"
p params
#user = User.where(id: params[:user_id])
end
This is my ajax function. I changed the url removing the last .xlsx and still doesn't work
$.ajax({
type: "get",
url: "/export_xlsx_report_table.xlsx",
dataType: "json",
data: { user_id: 1 },
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
}).done(function(data){
});
This is the html file. When I click the link_to function works good but clicking the js function goes to the controller but nothing happens.
<%= link_to "Exportar reporte", export_xlsx_report_table_path(:user_id => 1, format: "xlsx"), class: 'btn btn-default btn-sm' %>
<a onclick="report_sales.export_report()" class="btn blue"> EXPORTAR REPORTE</a>
Thank you for any help you can give me.
You can't download a file to disk from JS: it is a security concern.
Take a look at the blog post below for a good workaround -
http://johnculviner.com/post/2012/03/22/Ajax-like-feature-rich-file-downloads-with-jQuery-File-Download.aspx
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%>")
Normally we can use many ways to use ajax with validation like using validate plugin or using plain ajax(no rails ajax) like below
data using ajax requests
$.post("test.php", $("#testform").serialize());
In your situation it can be something like that.
$('#your_form').submit(function(event) {
event.preventDefault();
/* Email validation here.*/
$.ajax({
type: "POST",
url: $(this).attr('action'),
data: $(this).serialize(),
});
});
But, I wish to use client side validation with Real Rails ajax . Please help how to do this?
Maybe you'd like to use this gem
UPDATE 1:
Since you want to do this through ajax, validation can be handled through you controller.
# Let's assume your remote request hits index
def index
#object.create(params[:object])
respond_to do |format|
format.html # index.html.erb
format.js
end
end
Now, define an index.js.erb in the relevant views folder & render
<% if #object.new_record? %>
alert("Failed to upload record: <%= j #object.errors.full_messages.join(', ').html_safe %>");