I have datatable and a div, where I want to load information about the order with ajax after clicking on the row of the datatable.
assets/javascript/orders.js:
$(document).ready(function() {
$('#orders').dataTable({
"sPaginationType": "full_numbers",
"bJQueryUI": true
});
$('#orders tbody tr').each( function() {
this.onclick = function() {
$('#order_info').load(
???
)
};
})
})
orders/list.html.erb:
<table id="orders">
<tbody>
<% #orders.each do |order| %>
<tr data-orderrow=<%= order[:id] %>>
</tr>
<% end %>
</tbody>
</table>
<div id="order_info" style="float: right;">
</div>
If my approach is good, what should I put in the place of ??? in my javascript.
You can make an ajax call to some controller and action that returns json data. Then consume that data, and display the json that comes back.
$(document).ready(function() {
// best to 'cache' the jquery queries you use more than once.
var $orders = $("#orders"),
$orderInfo = $("#orderInfo");
$orders.dataTable({
"sPaginationType": "full_numbers",
"bJQueryUI": true
});
// it is best not to attach one click even to each 'tr', so instead
// I've added a single click event to the entire table, and then
// determine the closest 'tr' to get the orderId from it.
$orders.click(function(e){
var $target = $(e.target), $order, orderId; // get the element that was clicked
$order = $target.closest("tr");
orderId = $order.attr("data-orderrow");
$.getJSON("/orders/"+orderId+".json",function(orderData){
// do something with orderData, which is an object with the order data
});
});
})
Then in your controller, you could do something like this. You'll need to fill this out in the best way for your application:
class SomeController < ApplicationController
def show
order = Order.find(params[:id])
respond_to do |format|
format.html { render "show" }
format.json { render :json => order }
end
end
end
Related
I work on a project with Ruby on rails. I currently have some issues with my index page on which I use Datatables to display my data.
I use the server-side option to display my data since it's a very large database. I added a selector to allow the user to select a project whose vulnerabilities he wants to see. I managed to get the project in my controller and I passed it in an instance variable because I need it in my view. However, the variable isn't reloaded with the ajax call.
How can I reload the instance variables in my view ? I don't need only the project but also other variables such as the displayed variables to make graphs depending on the criticity...
Maybe I'm doing this the wrong way. Could you help me ? Below is my code.
View :
<div class="col-md-8 col-xs-8" style="display: inline-block">
<div class="x_panel">
<h4>Filtres</h4>
<div class="x_content">
<div class="col-md-6 col-xs-6" style="display: inline-block">
<label>Filter by projet : </label>
<%= select_tag "datatable_project", options_from_collection_for_select(#user.projects, :id, :name), include_blank: "Tous", onchange: "selectProject(this.value)" %>
</div>
</div>
</div>
</div>
<div class="x_panel">
<table id="datatable-history-external" class="table responsive-utilities mcs-datatable-history-external" width="100%">
<thead>
<tr class="headings">
<th class="column-title all" style="width:150px">CVE</th>
<th class="column-title all">Base score</th>
<% if not #selected_project.nil? and #selected_project.level == 2 %>
<th class="column-title all">Environmental score</th>
<% else %>
<th class="column-title never"></th>
<% end %>
<th class="column-title none">Description</th>
</tr>
</thead>
</table>
</div>
I want to display an new column if the level of the selected project is 2. I tried to change the class from the javascript but it didn't work since I use responsive to display a childRow.
Controller :
def pagination
# Recuperation of the DataTable parameters
draw = params[:draw]
longueur = params[:length]
start = params[:start]
# Call method from model to get validated vulnerabilities
vulnerability_ids = []
#get project selection from the user
selected_project_id = get_selection
if not selected_project_id.blank?
#selected_project = Project.find(selected_project_id)
vulnerability_ids = #selected_project.validated_vulnerabilities.pluck(:id)
else
#user.projects.each {|project| vulnerability_ids += project.validated_vulnerabilities.pluck(:id)}
end
vulnerability_ids = vulnerability_ids.uniq
#vulnerabilities = Vulnerability.where(id: vulnerability_ids)
result = []
result = #vulnerabilities.offset(start.to_i).limit(longueur.to_i)
recordsTotal = #vulnerabilities.length
# creation of the json that we want to return and display
result_json = create_datatable_json(result, draw, recordsTotal)
render :json =>result_json.to_json
end
Controller Helper :
def get_selections
parameters = request.query_parameters
selected_project = parameters["project"]
return selected_project
end
def create_datatable_json(result, draw, recordsTotal)
vulns = []
result.each do |vul|
cves = []
#if there is a selected project and it is of level 2 we want to display a column with the max environmental notation
if #selected_project.nil? or not #selected_project.analysis_level == 2
max_environmental_score = "NC"
else
max_environmental_notation = EnvironmentalNotation.get_max_environmental_notation(vul, #selected_project).environmental_score
end
if vul.notation
vuln = [:id => vul.id, :cve => first_cves, :base_score => vul.notation.base_score.to_s, :description => vul.description, :environmental_score => max_environmental_score.to_s]
else
vuln = [:id => vul.id, :cve => first_cves, :base_score => "NC", :description => vul.description, :environmental_score => max_environmental_score.to_s]
end
vulns += vuln
end
result_json = {
:draw => draw,
:recordsTotal => recordsTotal,
:recordsFiltered => recordsTotal,
:data => vulns
}
return result_json
end
Javascript :
// Select project to filter the vulnerabilities to display in table
function selectProject(value) {
project = value;
Turbolinks.visit(window.location);
}
$(document).on("turbolinks:load", function() {
$('.mcs-datatable-history-external').each(function() {
if ($.fn.dataTable.isDataTable($(this))){
$(this).destroy();
}
var url = "vulnerabilities/pagination;
if (project != null) {
url = url+"/?project="+project;
$('#datatable_project').val(project);
}
add_in_array(
$(this).DataTable({
processing: true,
serverSide: true,
stateSave: true,
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.childRowImmediate,
type: '',
target: 3
}
},
ajax: {
type: "POST",
format: "js",
url: url
},
columns: [
{ data: "cve", targets:0},
{ data: "base_score", targets:1, orderable: true, searchable: true},
{ data: "environmental_score", targets:2, orderable: true, searchable: false},
{ data: "description", className: "no-border-top", targets:3, orderable: false, searchable: true},
],
searching: true,
}));
})
}
I need to get #selected_project and #vulnerabilities in my view but they are nil.
I don't know if I'm clear enough. I've put a lot of code here. Don't hesitate to ask for some explanations if needed.
first make sure that the changes are saved to data base later if the instance is not reloaded then you can use
#instance_variable.reload
To reload the instance
You can use it whenever you need to display/use the updated data of instance. Say there is an instance #user and you have updated the details of user like #user.email in the db and later you need to display the email of user using that instance like #user.email. In such case the instance doesnt get reloaded and it will display the previous value.
So if you use #user.reload then rails will fetch the details of that instance from DB. so that there will be less scope for error
I have been stuck on this problem for quite some time now and looked through several posts as well, however I cannot achieve exactly what I want for my Rails application. Essentially, I want to be able to click on a table row on my page and have a modal pop up which displays all the information for that specific record. Here are the scenarios which I have thought of/attempted partially:
Set the data-link attribute in table row with some JS as follows
HTML:
<tr data-link="<%= kid_path %>">
...
</tr>
JS:
$("tr[data-link]").dblclick(function() {
window.location = $(this).data("link")
})
This worked fine to open the show path page generated by the scaffold, but I was not able to modify it to work with a modal and have the same data for the kid in the modal.
Use data-id and JavaScript to load onto the modal
HTML:
<tr data-id="<%= kid.id %>">
...
</tr>
JS:
$(function () {
$('#showModal').modal({
keyboard: true,
backdrop: "static",
show: false,
}).on('show', function () {
});
$(".table-striped").find('tr[data-id]').on('click', function () {
debugger;
$('#showDetails').html($('<p>' + 'Kid ID: ' + $(this).data('id') + '<%= Kid.find(30).first_name %>' + '</p>'));
$('#showModal').modal('show');
});
});
In this approach I am able to load the modal on row click and am able to access the Kid ID, however I cannot move further to access other attributes of the record. For example, I want to set #Kid = kid.find(id) using JS where id would be the extracted ID from the row. And then, I want to be able to write the generic modal template which displays other elements (ex. kid.first_name, kid.last_name, etc).
I am totally stuck and cannot find any approach that helps to accomplish my goal. Any help is appreciated, thank you.
You need to ajax call record attributes because the line Kid.find(30).first_name doesn't exist at the time page loaded.
Try this:
KidsController
def show
kid = Kid.find(params[:id])
respond_to do |format|
format.html { // Usually show.html.erb }
format.json do
# Return kid as json object
result = {
first_name: kid.first_name,
last_name: kid.last_name
}
# If you want to return the whole kid record attributes in json: result = kid.attributes.to_json
render json: result
end
end
end
Try /kid/[:id].json to verify that you are not getting UnknownFormat error.
JS
$(".table-striped").find('tr[data-id]').on('click', function () {
var kid_id = $(this).data('id');
$.getJSON("/kid/" + kid_id, function(data) {
// Render the modal body here
// first_name = data.first_name, last_name = data.last_name etc
$('#showDetails').html($('<p>'+ data.first_name + '</p>'));
$('#showModal').modal('show');
});
})
If you have setup correct route for Kid model then these are what you needed.
UPDATED: I made a typo in the result hash. FIXED
I'm currently learning rails and working on what I'm sure is everyone's first rails app, a simple todo list. I need to implement a checkbox next to the items to indicate whether they are complete or not. Each item has a boolean attribute called "completed" in their model. I have found a couple checkbox questions while searching but none explain the syntax very easily in the context of the index view.
Also, I really want the checkbox to work without a submit button. I know I could accomplish something like this using AngularJS's ng-model but I don't think it would be practical to implement angular for such a small thing and I don't know how angular works with rails.
If anyone could give me a pointer in the right direction, it would be greatly appreciated. Here's my index.html.erb for reference.
<h1>
To Do List
</h1>
<table>
<tr>
<% #todo_items.each do |item| %>
<!-- Checkbox here -->
<tc style="<%= 'text-decoration: line-through' if item.completed %>">
<%= link_to item.title, item %>
</tc>
<tc>
<%= item.description %>
</tc>
<tc>
<%= link_to "Edit", edit_todo_item_path(item) %>
</tc>
<tc>
<%= link_to "Delete",item, data:{:confirm => "Are you sure you want to delete this item?"}, :method => :delete %>
</tc>
<hr/>
<% end %>
</tr>
</table>
<p>
<%= link_to "Add Item", new_todo_item_path %>
</p>
This is my way, I don't know this way is right direction or not but this works for me (also different case but same of concept).
views for checkbox
You could put an id item or something into attribute of checkbox for find an object in controller if you send data to controller for get record of object, and you could define if attribute completed of record is true or false:
<%= check_box_tag :completed_item, 1, item.completed? ? true : false, { class: 'name-of-class', data: { id: item.id} } %>
controller
You need two action call set_completed and remove_completed, and also you don't need templates for them, just use format as json:
before_action :set_item, only [:set_completed, :remove_completed, :other_action]
def set_completed
#item.set_completed!
respond_to do |format|
format.json { render :json => { :success => true } }
end
end
def remove_completed
#item.remove_completed!
respond_to do |format|
format.json { render :json => { :success => true } }
end
end
private
def set_item
#item = Item.find params[:id]
end
Model
For set_completed! and remove_completed! you could define in your model
def set_default!
self.update_attributes(:completed => true)
end
def remove_default!
self.update_attributes(:completed => false)
end
routes
resources :address do
collection do
post 'set_completed'
post 'remove_completed'
end
end
Also, you need help JavaScript for handle send request from view to controller event click of checkbox:
jQuery
$(".completed_item").click(function(){
var check = $(this).is(":checked");
if (check == true){
set_completed($(this).attr('data-id'));
} else{
remove_completed($(this).attr('data-id'));
}
});
function set_completed(data_id) {
$.ajax({
type: 'POST',
url: "/items/set_completed",
data: { id: data_id},
dataType: 'json',
success: function(response){
if(response){
}else{
alert('error');
}
}
})
}
function remove_compelted(data_id) {
$.ajax({
type: 'POST',
url: "/items/set_completed",
data: { id: data_id},
dataType: 'json',
success: function(response){
if(response){
}else{
alert('error');
}
}
})
}
I am working in a Rails application and using the deferLoading: true option on jQuery DataTables in order pass the loading of the first DataTables to the Rails controller.
I have the datatable loading how I want it, loading the initial table in the controller gets rid of the Ajax delay when the initial html loads, however, the Datatable info section won't display the pagination results.
Code and images are shown below.
Again everything works except the pagination at the bottom of the table, I just cant get it to apply the same details as the Ajax calls to the datatable. Any ideas or direction on this issue would be greatly appreciated!
index.html.erb:
<div class="row">
<div class="col-xs-12 table-wrapper">
<div class="inner-wrapper">
<p class="quick-app">
<a class="custom-btn accent-inverse-btn add-user" href="<%= calculator_path%>">Quick Application</a>
</p>
<table class="table table-striped table-scroll cms-table-width dataTable" id="customer_deals_datatable" data-source="<%= dealer_customer_deals_url(:include_archived => params[:include_archived].present?) %>" >
<div>
<thead>
<tr>
<th>ID/Calculator</th>
<th>Applicant/Co-Applicant</th>
<th>Year</th>
<th>Model</th>
<th>App Status</th>
<th>Tier Number</th>
<th>Docs Status</th>
<th>Submitted On</th>
<th>Days Remaining</th>
<th>Chrome Decision</th>
<th>Updated At</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% #datatable.data.each do |datum| %>
<tr>
<% datum[0] = datum[0].join('') %>
<%= (datum.map {|content| "<td>#{content}</td>"}.join('')).html_safe %>
</tr>
<% end %>
</tbody>
</div>
</table>
</div>
</div> <!-- </div>#content -->
</div>
controller
def index
respond_to do |format|
format.html do
params.merge!({"iDisplayLength"=>"10","iSortCol_0"=>"10","sSortDir_0"=>"desc"})
#datatable = CustomerDeals::CustomerDealsDataTable.new(view_context, #dealer)
end
format.json { render json: CustomerDeals::CustomerDealsDataTable.new(view_context, #dealer) }
end
end
here is a portion of the code from the datatable class in the project:
module CustomerDeals
class CustomerDealsDataTable
def fetch_deal_searches
return #deal_searches if #deal_searches.present?
deal_searches = CustomerDeals::CustomerDealSearch.where(dealership_id: #dealer )
if is_submitted_on_sort?
deal_searches = deal_searches.where('deal_dated_calculator_value != ?', 'calculator')
end
if params[:sSearch].present?
deal_searches = deal_searches.containing(params[:sSearch])
end
deal_searches = deal_searches.order(order_query)
#deal_searches = deal_searches
end
def is_submitted_on_sort?
SORT_COLUMNS[params[:iSortCol_0].to_i] == 'deal_submitted_on'
end
def lookup_sort_column
SORT_COLUMNS[params[:iSortCol_0].to_i]
end
def order_query
"#{lookup_sort_column} #{params[:sSortDir_0]}"
end
def paged_deal_searches
fetch_deal_searches.page(current_page_number).per(params[:iDisplayLength])
end
def current_page_number
params[:iDisplayLength].to_i == 0 ? 1 : params[:iDisplayStart].to_i/params[:iDisplayLength].to_i + 1
end
end
end
You're on the right track, deferLoading also can be assigned integer or array of two integers to specify how many records there are in the table for pagination to work.
From the manual:
deferLoading is used to indicate that deferred loading is required, but it is also used to tell DataTables how many records there are in the full table (allowing the information element and pagination to be displayed correctly).
In the case where a filtering is applied to the table on initial load, this can be indicated by giving the parameter as an array, where the first element is the number of records available after filtering and the second element is the number of records without filtering (allowing the table information element to be shown correctly).
Examples:
57 records available in the table, no filtering applied:
$('#example').dataTable( {
"serverSide": true,
"ajax": "scripts/server_processing.php",
"deferLoading": 57
} );
57 records after filtering, 100 without filtering (an initial filter applied):
$('#example').dataTable( {
"serverSide": true,
"ajax": "scripts/server_processing.php",
"deferLoading": [ 57, 100 ],
"search": {
"search": "my_filter"
}
} );
I'm trying to ajaxify my will_pagniate pagination in rails. I want to have the old page fade out and the new one fade in.
Here's the relevant part of my controller:
respond_to do |format|
format.html # new.html.erb
format.js {
render :update do |page|
page.replace 'page', :partial => 'cur_page'
end
}
format.xml { render :xml => #branch }
end
The aforementioned partial:
<div id="page">
<%= will_paginate %>
<div id="posts">
<%= render #posts %>
</div>
<%= will_paginate %>
</div>
And the relevant part of application.js:
document.observe("dom:loaded", function() {
// the element in which we will observe all clicks and capture
// ones originating from pagination links
var container = $(document.body)
if (container) {
var img = new Image
img.src = '/images/spinner.gif'
function createSpinner() {
return new Element('img', { src: img.src, 'class': 'spinner' })
}
container.observe('click', function(e) {
var el = e.element()
if (el.match('.pagination a')) {
el.up('.pagination').insert(createSpinner())
target = $('posts')
new Effect.fade(target, { duration: 0.3, afterFinish: function()
{
new Ajax.Request(el.href,
{
method: 'get',
onSuccess: function(){ new Effect.Appear(target, {duration:0.3})}
})
}})
e.stop()
}
})
}
})
The script seems to get killed on this line,
new Effect.fade(target, { duration: 0.3, afterFinish: function()
because I see the spinner.gif start, then no fading and the page is refreshed normally. I have got the ajax working before I tried to add Effect.Fade and Effect.Appear.
Is this the right way to go about this? Should I put the effects in the controller instead?
Here is what I did using jQuery and working well too :)
Put your will_paginate helper view call in a div
#tickets_pagination
= will_paginate #tickets
In application.js
$("#tickets_pagination .pagination a").live("click", function() {
$.get("/users/?"+this.href.split("?")[1], null, null, "script");
return false
});
The javascript above will convert the pagination links in #tickets_pagination to ajax links
In your controller as usual
def index
#tickets = Ticket.all.paginate({:page => params[:page], :per_page => 10 })
respond_to do |format|
format.js
format.html
end
end
Now finally in index.js.erb
$("#tickets_list_table").fadeOut('slow');
$("#tickets_list_table").html("<%= escape_javascript(render :partial =>'tickets/tickets_table', :locals => {:tickets => #tickets}) %>");
$("#tickets_list_table").fadeIn('slow');
Here tickets/ticket_table has a table that lists all tickets. The partial is rendered in a div #ticket_list_table
Hope this will work for you as well.
I tried putting more of the work into the javascript helpers:
respond_to do |format|
format.html # new.html.erb
format.js {
render :update do |page|
page.visual_effect :fade, 'posts', :afterFinsh => "function(){" +
page.replace 'page', :partial => 'cur_page' +
page.visual_effect(:appear, 'branches') + "}"
end
}
format.xml { render :xml => #branch }
end
Then removed this part of the javascript:
new Effect.fade(target, { duration: 0.3, afterFinish: function()
I get the effect I want, but all out of order. The request completes and the html is replaced, then the div fades out and then reappears!
Not very familiar with RoR, does it generate its own client-side JS that may possibly be battling your code?
If not, I would say the problem is somewhere in your own client-side code. For testing, get rid of the HREF attribute from the anchor tag and place the URL as a string literal in the Ajax request. If nothing happens, there is a problem with the Ajax request itself. If the page loads as expected, then the event in the original scenario is not being completely stopped.
Also, clean up your JS a bit just to be sure, line-ending semi-colons where needed.
You seem to mix up things a bit.
Either you write $('posts').fade or new Effect.fade('posts').
Secondly, i can't seem to find the afterFinish option in the documentation.
So i would suggest something along the following lines:
container.observe('click', function(e) {
var el = e.element()
if (el.match('.pagination a')) {
el.up('.pagination').insert(createSpinner())
target = $('posts')
new Effect.fade('posts', { duration: 0.3});
setTimeout("new Ajax.Request(el.href, { method: 'get',
onSuccess: function(){
new Effect.Appear('posts', {duration:0.3})
} })", 1000);
e.stop();
}
})
Hope this helps.