I started working on AngularJS and DataTables and wonder whether it is possible to customize the response DataTables is expecting. The current expectation of the DataTables plugin is something like this:
{
"draw": 1,
"recordsTotal": 57,
"recordsFiltered": 5,
"data": [...]
}
On the server end, the API's are being handled by django-tastypie
The response from server is:
{
meta: {
limit: 20,
next: null,
offset: 0,
previous: null,
total_count: 2
},
objects: [...]
}
So, is there a way to tweak Datatables Plugin to accept/map this response, or I'll have to find a way to add expected fields to the api?
So far I've done this:
var deptTable = angular.element('#deptManagementTable').DataTable({
processing: true,
serverSide: true,
pagingType: "simple_numbers",
ajax: {
url: "/client/api/v1/departments/",
data: function(d) {
d.limit = d.length;
d.offset = d.start;
d.dept_name__icontains = d.search.value;
},
dataSrc: function(json) {
for (var i=0, len=json.objects.length ; i<len ; i++) {
json.objects[i].DT_RowId = json.objects[i].dept_id;
}
return json.objects;
}
},
aLengthMenu: [
[5, 25, 50, 100],
[5, 25, 50, 100]
],
iDisplayLength: 5,
columns: [
{
data: "dept_name"
},
{
data: "dept_created_on",
render: function ( data, type, full, meta ) {
var dateCreated = new Date(data);
dateCreated = dateCreated.toLocaleDateString();
return dateCreated;
}
}
]
});
Any help will be appreciated.
Thanks in Advance :)
You can pass a function to the DataTables ajax option, this will give you full control over how to fetch and map the data before passing it to DataTables.
.DataTable({
serverSide: true,
ajax: function(data, callback, settings) {
// make a regular ajax request using data.start and data.length
$.get('/client/api/v1/departments/', {
limit: data.length,
offset: data.start,
dept_name__icontains: data.search.value
}, function(res) {
// map your server's response to the DataTables format and pass it to
// DataTables' callback
callback({
recordsTotal: res.meta.total_count,
recordsFiltered: res.meta.total_count,
data: res.objects
});
});
}
});
The solution above has been tested with jQuery DataTables 1.10.4.
As this question is tagged with Angular, here's a solution for those using angular-datatables.
<div ng-controller="testCtrl">
<table datatable dt-options="dtOptions" dt-columns="dtColumns" class="row-border hover"></table>
</div>
.controller('testCtrl', function($scope, $http, DTOptionsBuilder, DTColumnBuilder) {
$scope.dtOptions = DTOptionsBuilder.newOptions()
.withOption('serverSide', true)
.withOption('ajax', function(data, callback, settings) {
// make an ajax request using data.start and data.length
$http.get('/client/api/v1/departments/', {
limit: data.length,
offset: data.start,
dept_name__icontains: data.search.value
}).success(function(res) {
// map your server's response to the DataTables format and pass it to
// DataTables' callback
callback({
recordsTotal: res.meta.total_count,
recordsFiltered: res.meta.total_count,
data: res.objects
});
});
})
.withDataProp('data'); // IMPORTANT¹
$scope.dtColumns = [
// your column definitions here
];
});
The solution above has been tested with angular-datatables 0.3.0 + DataTables 1.10.4.
¹ The important part to note here is that the angular-datatables solution requires .withDataProp('data') to work, while the pure jQuery DataTables solution does not have a data: 'data' option.
This answer was very useful, but seems a bit outdated in the context of the current (1.10.12 at the moment) version of datatables, which actually makes life a lot easier (or at least more readable).
Under the current version you can do something like the following in your declaration (bearing in mind that tastypie needs to have the filterable & ordering options set for the fields you want to use).
You can now access the data being submitted in the ajax request by doing data.attr inside the function.
This assumes you wish to restrict searching to one field, but can easily be extended in the same manner do console.log(data) within the ajax function to see what is sent.
var table = $('#tableName').DataTable({
"deferRender":true,
"serverSide": true,
"ajax": function(data, callback, settings) {
var sort_column_name = data.columns[data.order[0].column].data.replace(/\./g,"__");
var direction = "";
if (data.order[0].dir == "desc") { direction = "-"};
$.get('your/tasty/pie/url?format=json', {
limit: data.length,
offset: data.start,
your_search_field__searchattr: data.search.value,
order_by: direction +sort_column_name
}, function(res) {
callback({
recordsTotal: res.meta.total_count,
recordsFiltered: res.meta.total_count,
data: res.objects
});
});
},
"columns": [
{ "data":"column_1", "orderable": false },
{ "data":"column_2" },
{ "data":"column_3" }
],
"order": [[1, "asc"]]
});
Related
I am trying to use Framework 7 autocomplete feature with youtube search API v3. I had used search api for autocomplete using Jquery UI. Framework 7 has also Ajax autocomplete feature. But my code is not working with Framework 7.
Here is my youtube search autocomplete js code for jquery UI, that works 100% and shows up video search suggestion on text input
//code for auto complete using jquery UI works perfect
jQuery( "#vid-search" ).autocomplete({
source: function( request, response ) {
//console.log(request.term);
var sqValue = [];
jQuery.ajax({
type: "POST",
url: "http://suggestqueries.google.com/complete/search?hl=en&ds=yt&client=youtube&hjson=t&cp=1",
dataType: 'jsonp',
data: jQuery.extend({
q: request.term
}, { }),
success: function(data){
console.log(data[1]);
obj = data[1];
jQuery.each( obj, function( key, value ) {
sqValue.push(value[0]);
});
response( sqValue);
}
});
},
select: function( event, ui ) {
setTimeout( function () {
youtubeApiCall();
}, 300);
}
});
And here is my youtube search autocomplete code with framework 7, Doest show up video search suggestions on text inpute..
var autocompleteDropdownAjax = myApp.autocomplete({
input: '#autocomplete-dropdown-ajax',
openIn: 'dropdown',
preloader: true, //enable preloader
valueProperty: 'id', //object's "value" property name
textProperty: 'name', //object's "text" property name
limit: 20, //limit to 20 results
dropdownPlaceholderText: 'Try "JavaScript"',
expandInput: true, // expand input
source: function (autocomplete, query, request, response, render) {
var results = [];
if (query.length === 0) {
render(sqValue);
return;
}
// Show Preloader
autocomplete.showPreloader();
// Do Ajax request to Autocomplete data
$$.ajax({
type: "POST",
url: "http://suggestqueries.google.com/complete/search?hl=en&ds=yt&client=youtube&hjson=t&cp=1",
dataType: 'jsonp',
data: jQuery.extend({
q: request.term
}, { }),
success: function (data) {
// Find matched items
console.log(data[1]);
obj = data[1];
jQuery.each( obj, function( key, value ) {
sqValue.push(value[0]);
});
response( sqValue);
// Hide Preoloader
autocomplete.hidePreloader();
// Render items by passing array with result items
render(sqValue);
}
});
},
select: function( event, ui ) {
setTimeout( function () {
youtubeApiCall();
}, 300);
}
});
That is because the jSon result should be in a special format, the result rendered by the API looks like:
["funn",["funny vines","funny videos 2016","funny videos","funny","funnel vision","funny cat videos","funny fails","funny pranks","funny cats","funny songs"]]
First parent array has the query you typed, second child array has the result array, you must push the child array to the result.
for (var i = 0; i < data[1].length; i++) {
results.push(data[1][i])
}
And here is the result:
Full code:
var autocompleteDropdownAjax = myApp.autocomplete({
input: '#autocomplete-dropdown-ajax',
openIn: 'dropdown',
preloader: true, //enable preloader
valueProperty: 'value', //object's "value" property name
textProperty: 'text', //object's "text" property name
limit: 20, //limit to 20 results
dropdownPlaceholderText: 'Search Youtube',
expandInput: true, // expand input
source: function(autocomplete, query, render) {
var results = [];
var returned = [];
if (query.length === 0) {
render(results);
return;
}
// Show Preloader
autocomplete.showPreloader();
// Do Ajax request to Autocomplete data
$$.ajax({
url: 'http://suggestqueries.google.com/complete/search?client=firefox&ds=yt',
method: 'GET',
crossDomain: true,
dataType: 'json',
//send "query" to server. Useful in case you generate response dynamically
data: {
q: query
},
success: function(data) {
// Find matched items
for (var i = 0; i < data[1].length; i++) {
results.push(data[1][i])
}
// Hide Preoloader
autocomplete.hidePreloader();
// Render items by passing array with result items
render(results);
}
});
}
});
I hope this helps :)
I want to load items by page since I have tables with large amount of data, but I don't want to load items for each page once the user clicks it.
Instead, I rather preload 1000 items (for example) ahead and only fetch more results if the user moves to a page I still didn't fetch the data for.
Is it possible?
I found a way to solve it.
Here is the basic logic:
Create a local data cache object that will hold arrays of results for each page.
When fetching data from the server, always return data for a few pages ahead and store them in the local cache object
Write a method for the controller.loadData that will check to see if you have the desired page results in the local cache object, if so - return that array, if not - return a promise that will fetch the results with some extra data for a few pages ahead.
An example of the local cache object snapshot:
{
"1": [{name: "ff"}, {name: "fdd"}],
"2": [{name: "fds"}, {name: "dsr"}],
"3": [{name: "drr"}, {name: "ssr"}]
}
script section
<script type="text/javascript">
$(document).ready(function () {
List();
});
function List() {
//$(function () {
loadjsgrid();
$("#jsGrid").jsGrid({
height: "auto",
width: "100%",
filtering: true,
editing: false,
sorting: true,
autoload: true,
paging: true,
pageSize: 10,
pageButtonCount: 5,
pageLoading: true,
controller: {
loadData: function (filter) {
var startIndex = (filter.pageIndex - 1) * filter.pageSize;
var d = $.Deferred();
$.ajax({
type: 'GET',
url: '#Url.Action("[ActionName]", "[Controllername]")',
data: filter,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (data) {
if (data.Message == "Failed") {
data.Result = [];
data.Count = 0;
}
console.log(data);
d.resolve(data);
}
});
return d.promise().then(function (q) {
return {
data: q.Result,
itemsCount: q.Count
}
});
}
},
},
fields: [
{ name: "rct_no", type: "text", title: 'Serial Number', autosearch: true, width: '10%' }
],
});
$("#pager").on("change", function () {
var page = parseInt($(this).val(), 10);
$("#jsGrid").jsGrid("openPage", page);
});
}
Controller section
public ActionResult getList(int pageIndex = 1, int pageSize = 10)
{
try
{
var query = #" from rd_receipt_header
var irList = DAL.db.Fetch<[className]>(pageIndex, pageSize, #"select * " + query );
var count = DAL.db.ExecuteScalar<int>("select count(*) " + query);
return Json(new { Message = "Success", Result = irList, Count = count }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex) { return Json(new { Message = "Failed", Result = ex.Message }, JsonRequestBehavior.AllowGet); }
I am using ajax to get data from server as ajax format
Can someone post a demo or a piece of code, to exemplify how to use jqpivot and loading data using ajax.
Thank you.
I would recommend you to examine the source code of free jqGrid. Look at the part of the code. It looks like
jqPivot: function (data, pivotOpt, gridOpt, ajaxOpt) {
return this.each(function () {
var $t = this, $self = $($t), $j = $.fn.jqGrid;
function pivot(data) {
...
}
if (typeof data === "string") {
$.ajax($.extend({
url: data,
dataType: "json",
success: function (data) {
pivot(jgrid.getAccessor(data, ajaxOpt && ajaxOpt.reader ? ajaxOpt.reader : "rows"));
}
}, ajaxOpt || {}));
} else {
pivot(data);
}
});
}
You will get practically directly the answer on your question. You need specify the URL to the server which provides the input data in JSON form. No other data format is supported ("xml", "jsonp" and so on) directly, but you can use ajaxOpt parameter to overwrite the parameters of the Ajax call. It's important to understand additionally, that jqGrid uses $.jgrid.getAccessor method to read the data from the server response. The default data format should be the following:
{
"rows": ...
}
where the value of "rows" should have the same format as the data parameter of jqPivot if you use if without Ajax. If you have for example
{
{
"myRoot": {
"myData": ...
}
}
}
then you can use 4-th parameter of jqPivot (ajaxOpt) as
{ reader: "myRoot.myData" }
If the response from the server is array of data:
[
...
]
or it have some non-standard form than you can use function form of reader. For example
$("#grid").jqGrid("jqPivot", "/myUrl", {
xDimension: [{...}],
yDimension: [{...}, ...],
aggregates: [{...}],
},
{
iconSet: "fontAwesome",
cmTemplate: { autoResizable: true, width: 80 },
shrinkToFit: false,
autoResizing: { compact: true },
pager: true,
rowNum: 20,
rowList: [5, 10, 20, 100, "10000:All"]
},
{
reader: function (obj) { return obj; },
contentType: "application/json; charset=utf-8",
type: "POST",
error: function (jqXHR, textStatus, errorThrown) {
alert('HTTP status code: ' + jqXHR.status + '\n' +
'textStatus: ' + textStatus + '\n' +
'errorThrown: ' + errorThrown);
alert('HTTP message body (jqXHR.responseText): ' + '\n' + jqXHR.responseText);
}
});
The above code specify the reader at the function which use all the response data directly (without object with rows property). It specifies contentType and type parameter of the jQuery.ajax and the callback function error.
If all the options seems you too difficult you can load the data directy in your code with direct call of jQuery.ajax for example and the call jqPivot after the data are loaded (inside of success callback or done).
I am having trouble updating hidden inputs using data retrieved from Select2 Ajax.
Please see my code:
var select2_town = $('.select-test select').select2({
ajax: {
url : ajax_var.url,
dataType: 'json',
type: 'post',
delay: 250,
data: function (params) {
return {
q: params.term, // search term
page: params.page,
};
},
processResults: function (data, page) {
return {
results: $.map(data, function (item) {
return {
id: item.id, //eg 1149
town: item.place_name, //eg Reading
county: item.county, //eg Berkshire
country: item.country,
data: item
};
})
};
},
cache: true;
},
escapeMarkup: function (markup) { return markup; },
minimumInputLength: 1,
templateResult: function (item) { return ('<p>' + item.town + ' - ' + item.county + '</p>'); },
templateSelection: function (item) { return (item.town); },
});
This code works fine for me. My issue is what happens after a value is selected.
I would like to update my hidden input ids "#town","#county" and "#country" with town,county and country respectively through an 'change' event.
I have seen many SO examples but they all stop at $(this).select2('data')[0];but do not expand on it.
Weird thing is that the following script displays the correct value in console log. but ALWAYS only apply the object id to #country.
select2_town.on('change', function() {
var obz = $(this).select2('data')[0].country;
console.log(obz);//displays Reading in console
$("#country").attr("value",obz); //displays 1149
});
This actually works.
I had another function that was adding the ID hidden in another file. I deleted that and then the script worked as I wanted it to.
Regards
Thanks
So I have a javascript function that runs on page load, listed below:
function createGrid()
{
var myGrid =
jQuery("#responseMessages"),
reportBtn = "<input style='height:22px;width:100px;' type='button' value='Report' />",
getColumnIndexByName = function(grid,columnName) {
var cm = grid.jqGrid('getGridParam','colModel');
for (var i=0,l=cm.length; i<l; i++) {
if (cm[i].name===columnName) {
return i; // return the index
}
}
return -1;
};
myGrid.jqGrid({
url: "<%= Url.Action("GetMessages", "Home") %>",
datatype: 'json',
myType: 'GET',
height: 'auto',
colModel: [
{ name:'distance', index:'distance', label:'Distance', width:100 },
{ name:'age', index:'age', label:'Age', width:75 },
{ name:'message', index:'message', label:'Message', width:500 },
{ name:'messageId', index:'messageId', key:true, hidden:true },
{ name:'report', index:'report', label: 'Report', width:100,
formatter:function() { return reportBtn; } }
],
loadComplete: function() {
var i=getColumnIndexByName(myGrid,'report');
// nth-child need 1-based index so we use (i+1) below
$("tbody > tr.jqgrow > td:nth-child("+(i+1)+") > input",myGrid[0]).click(function(e) {
var tr=$(e.target,myGrid[0].rows).closest("tr.jqgrow");
var x=window.confirm("Are you sure you want to report this message?")
if (x)
{
reportMessage(tr[0].id);
}
e.preventDefault();
});
},
rowNum:25,
viewrecords:true,
rowList:[10,25,50],
pager: '#pager',
caption: "What's going on in your area!"
});
}
Now it loads the grid fine, actually makes a call to the public ActionResult GetMessages() on the server correctly, and doesn't receive any data from the response, so it doesn't fill the grid and says there are no records. Yay!
Problem is, I click a button on the page, which triggers this javascript method:
function reloadGrid()
{
$("#responseMessages").trigger("reloadGrid");
}
So the grid goes and re-gets the server method, yay! But this time, the server sends a response back that looks like this from firebug:
{"ContentEncoding":null,"ContentType":null,"Data":{"page":1,"records":2,"rows":[{"id":3,"cell":["\u003c 1 mile","25 hour(s)","sdfgsdfgsdfg","3"]},{"id":2,"cell":["\u003c 1 mile","25 hour(s)","adfg","2"]}],"total":1},"JsonRequestBehavior":1}
However, the grid doesn't fill and still says there are no records, when there should be 3.
You use not standard format of JSON data, so you should include the corresponding jsonReader parameter in the jqGrid which describe how jqGrid should get the data from the JSON input:
jsonReader: {
page: "Data.page",
total: "Data.total",
records: "Data.records",
root: "Data.rows"
}
How you can read from the demo the data will be read after the change.