Why does jqGrid not show 2nd page when data is loaded dynamically? - javascript

I have prepared a jsFiddle which simulates to load data into jqGrid from an echo page. This is the example:
jqGrid Paging Example (click to run)
$(function() {
var gridSampleData = [
{ id: 10, firstName: "Jane", lastName: "Doe1"},
{ id: 20, firstName: "Justin", lastName: "Time1" },
{ id: 30, firstName: "Jane", lastName: "Doe2"},
{ id: 40, firstName: "Justin", lastName: "Time2" },
{ id: 11, firstName: "Jane", lastName: "Doe3"},
{ id: 21, firstName: "Justin", lastName: "Time3" },
{ id: 31, firstName: "Jane", lastName: "Doe4"},
{ id: 41, firstName: "Justin", lastName: "Time4" }
];
var rowsPerPage = 4;
var numRows = gridSampleData.length;
var pagedData = {
page:1, total:numRows/rowsPerPage, records: numRows, rows: gridSampleData
}
// simulate AJAX request: use echoUrl instead of real web service
var jsonData = JSON.stringify(pagedData);
var echoUrl = '/echo/js/?js=' + jsonData;
$("#Grid4").jqGrid({ scroll: false, gridview: true,
pager: '#pagerGrid4', rowNum: rowsPerPage, viewrecords: true,
height: 90, width: 400,
colNames: ['First name', 'Last name'],
colModel: [{name: "firstName"}, {name: "lastName"}],
datatype: "json",
jsonReader: {
page: "page",
total: "total",
records: "records",
root: "rows",
repeatitems: false
},
mtype: 'POST',
url: echoUrl
});
});
HTML:
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>Canonical jqGrid example</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/themes/redmond/jquery-ui.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/free-jqgrid/4.15.2/css/ui.jqgrid.min.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/free-jqgrid/4.15.2/jquery.jqgrid.min.js"></script>
<table id="Grid4"></table>
<table id="pagerGrid4"></table>
Everything looks fine, until you click on the navigation buttons - it will not show you the 2nd page. Why is this? If you load data statically, as grid #4 in this example, it works fine.
Note: I had to use jsFiddle, because StackOverflow currently does not support an echo page in the snippet editor.

It is because in your example you are returning all rows of data but always sending page: 1. Each request the grid makes it's told that it is getting data for page 1 it never gets data for page 2. To make your example work you can set the property loadonce to true as I have done in a fork of your original code.
Example Fiddle
Or you could rework your code to send the first 4 rows and page: 1 and when the request comes for page 2 send the last 4 rows and page: 2

If you want really simulate loading the data from the server without loadonce: true then I'd recommend you to use serializeGridData, which send json parameter (see the documentation of Echo service) with the corresponding page of data. One can use slice on the array of source data.
serializeGridData: function (request) {
var start = (request.page - 1) * request.rows;
return {
json: JSON.stringify({
page: request.page,
total: gridSampleData.length/request.rows,
records: gridSampleData.length,
rows: gridSampleData.slice(start, start + request.rows)
})
}
}
See the corresponding modified demo https://jsfiddle.net/OlegKi/bny6h1nz/2/. One should remark, that the server should typically return sorted data based on sidx and sord parameters (request.sidx and request.sord). One can implement the feature too using sort method of array, which has callback function, but I suppose, you don't want to implement full simulation of server behavior required for jqGrid in JSFiddle.

Related

Mimic Excel copy/paste in Tabulator

I am trying to mimic Excel's copy/paste within Tabulator. Simply put, I want to copy (ctrl+c) a couple of cell values and then paste them somewhere else in the table.
My idea is to:
copy a couple of cells
click on the cell where I want to start pasting from.
Store that cell's object somewhere so I can reference it when I paste the data
Ctrl+v or right click paste
Using the clicked cell I have stored as well as clipboardPasteParser callback, I would like to build a rowData object to return from that callback (as described in the docs).
Using the clipboardPasteAction callback, I will take that rowData object and use it to update the cells.
However, as far as I understand, Tabulator's clipboard functionality does not facilitate this kind of copy/paste action. In the below code snippet you will see that I console.log out the result of paste in the clipboardPasteParser(clipboard) callback. clipboard is just a concatenated string of the copied values.
For example, copying "Mary May", "1", "female", "2", and "blue", results in the string "Mary May1female2blue" in the clipboard callback variable.
Here is the code if you would like to try it out.
var data = [
{
id: 1,
name: 'Mrs. Rafaela Barton',
progress: 50,
gender: 'male',
height: 4,
col: 'GhostWhite',
dob: '02/10/1986',
driver: false
},
{
id: 2,
name: 'Grant Kunze',
progress: 16,
gender: 'female',
height: 4,
col: 'DarkViolet',
dob: '05/07/1993',
driver: true
},
{
id: 3,
name: 'Marilou Morar',
progress: 30,
gender: 'female',
height: 2,
col: 'Turquoise',
dob: '14/10/1984',
driver: true
},
{
id: 4,
name: 'Nathan Fadel Jr.',
progress: 78,
gender: 'female',
height: 4,
col: 'SkyBlue',
dob: '01/11/1921',
driver: false
},
{
id: 5,
name: 'Mrs. Amya Eichmann',
progress: 44,
gender: 'female',
height: 3,
col: 'Aquamarine',
dob: '20/02/1937',
driver: false
}
];
var table = new Tabulator("#example-table", {
height:"311px",
data: data,
columns:[
{title:"Name", field:"name", width:150},
{title:"Height", field:"height", bottomCalc:"sum"},
{title:"Progress", field:"progress", formatter:"progress", sorter:"number"},
{title:"Gender", field:"gender"},
{title:"Rating", field:"rating", formatter:"star", hozAlign:"center", width:100},
{title:"Favourite Color", field:"col"},
{title:"Date Of Birth", field:"dob", hozAlign:"center", sorter:"date"},
{title:"Driver", field:"car", hozAlign:"center", formatter:"tickCross"},
],
clipboard:true,
clipboardPasteParser: function(clipboard) {
console.log(clipboard);
},
});
<!DOCTYPE HTML>
<html>
<head>
<link href="https://unpkg.com/tabulator-tables#4.9.3/dist/css/tabulator.min.css" rel="stylesheet">
<script type="text/javascript" src="https://unpkg.com/tabulator-tables#4.9.3/dist/js/tabulator.min.js"></script>
</head>
<body>
<div id="example-table"></div>
</body>
</html>
Is there any way that I can get the callback parameter to somehow separate the values so I can parse it in the clipboardPasteParser callback? Is that possible? Am I perhaps missing something?
I have sneaking suspicion that this problem has less to do with Tabulator and more about how the browser handles copy. It would still be a nice feature if Tabulator was able to handle this natively.
Thank you!!
This is currently not possible, but cell selection will be coming in the 5.0 release in a couple of months

Javascript library for table rendering

I need to show an array of objects in the table like representation. Table has columns with the properties, and when clicked on the column it should show more data inside the table. It should be sortable.
Is there a JS library that could do this, so I dont have to write this from scratch?
Please see the attached image with the JSON object.
When the user clicks on Ana, additional row is inserted.
I created the demo https://jsfiddle.net/OlegKi/kc2537ty/1/ which demonstrates the usage of free jqGrid with subgrids. It displays the results like
after the user clicks on the "+" icon in the second line.
The corresponding code you can find below
var mydata = [
{ id: 10, name: "John", lname: "Smith", age: 31, loc: { location: "North America", city: "Seattle", country: "US" } },
{ id: 20, name: "Ana", lname: "Maria", age: 43, loc: { location: "Europe", city: "London", country: "UK" } }
];
$("#grid").jqGrid({
data: mydata,
colModel: [
{ name: "name", label: "Name" },
{ name: "lname", label: "Last name" },
{ name: "age", label: "Age", template: "integer", align: "center" }
],
cmTemplate: { align: "center", width: 150 },
sortname: "age",
iconSet: "fontAwesome",
subGrid: true,
subGridRowExpanded: function (subgridDivId, rowid) {
var $subgrid = $("<table id='" + subgridDivId + "_t'></table>"),
subgridData = [$(this).jqGrid("getLocalRow", rowid).loc];
$("#" + subgridDivId).append($subgrid);
$subgrid.jqGrid({
idPrefix: rowid + "_",
data: subgridData,
colModel: [
{ name: "location", label: "Localtion" },
{ name: "city", label: "City" },
{ name: "country", label: "Country" }
],
cmTemplate: { align: "center" },
iconSet: "fontAwesome",
autowidth: true
});
}
});
Small comments to the code. Free jqGrid saves all properties of input data in data parameter. I added id property to every item of input data. It's not mandatory, but it could be helpful if you would add more functionality to the grid. See the introduction for more details.
The columns are sortable based on the type of the data specified by sorttype property of colModel. To simplify usage some standard types of data free jqGrid provides some standard templates which are shortcurts for some set of settings. I used template: "integer" in the demo, but you could replace it to sorttype: "integer" if only sorting by integer functionality is important.
If the user click on "+" icon to expand the subgrid then jqGrid inserts new row and creates the div for the data part of the subgrid. You can replace subGridRowExpanded from above example to the following
subGridRowExpanded: function (subgridDivId) {
$("#" + subgridDivId).html("<em>simple subgrid data</em>");
}
to understand what I mean. The unique id of the div will be the first parameter of the callback. One can create any common HTML content in the subgrid. Thus one can create empty <table>, append it to the subgrid div and
then convert the table to the subgrid.
To access to the item of data, which corresponds to the expanding row one can use $(this).jqGrid("getLocalRow", rowid). The return data is the item of original data. It has loc property which we need. To be able to use the data as input for jqGrid we create array with the element. I's mostly all, what one have to know to understand how the above code works.
You can add call of .jqGrid("filterToolbar") to be able to filter the data or to add pager: true (or toppager: true, or both) to have the pager and to use rowNum: 5 to specify the number of rows in the page. In the way you can load relatively large set of data in the grid and the user can use local paging, sorting and filtering. See the demo which shows the performance of loading, sorting and filtering of the local grid with 4000 rows and another one with 40000 rows. All works pretty quickly if one uses local paging and not displays all the data at once.
I use datatables.net for all my "more complex than lists"-tables. I It's a very well kept library with loads of features and great flexibility.
In the "con" column I would say that it's so complex that it probably has quite a steep learning curve. Although the documentation is great so there is always hope for most problems.

Custom paging with array of JavaScript objects

I am working on a application which is nicely modularized using requirejs. One of the modules called data service is in charge of providing other modules with data. Pretty much all get* methods of this module return javascript script objects in the the following format:
res = {
totalRows: 537,
pageSize: 10,
page: 15,
rows: [
{
id: 1,
name: 'Angelina'
...
},
{
id: 2,
name: 'Halle'
...
},
{
id: 3,
name: 'Scarlet'
...
},
{
id: 4,
name: 'Rihanna'
...
},
{
id: 5,
name: 'Shakira'
...
},
....
//10 rows
{
id: 10,
name: 'Kate'
...
}
]
}
Is it possible to initialize the data table by providing it with rows for the current page, current page number, page size and the total number of records or pages so that it "knows" which page is currently being displayed as well as the number of available pages. Which in turn would allow the DT to build the pager correctly allowing the user to navigate to other pages in which case we would make another call to data service module to retrieve data from the database for the selected page.

Slickgrid - Replacing all grid data from AJAX source

In the app I'm building, I have a data grid and some select boxes where the user can set filters and upon a selection it makes an AJAX call to get a new array of data from the server.
I have the grid initializing with default filters, but I can't figure out how to wipe the grid of all rows, and re-populate with a fresh array. I was trying dataView, but after reading some posts this seems to not be the answer. I find the official example-6 (ajax example) confusing.
I would like column sorting and column re-ordering to be retained when new data is loaded.
Here is the js I currently have which only initializes properly:
$(function(){
//update the grid when select values change
$('#qol_options :input').change(function(){
update_grid_data();
});
init_grid = function(){
// set grid options
var grid;
var columns = [
{id: "village", name: "Village", field: "village", sortable: true},
{id: "setting", name: "Setting", field: "setting", sortable: true},
{id: "hood", name: "N.hood", field: "hood", sortable: true},
{id: "timespan", name: "Time", field: "timespan", sortable: true},
{id: "count_0", name: "0", field: "count_0", sortable: true, width: 10},
{id: "count_1", name: "1", field: "count_1", sortable: true, width: 10},
{id: "count_2", name: "2", field: "count_2", sortable: true, width: 10},
{id: "count_3", name: "3", field: "count_3", sortable: true, width: 10},
{id: "count_4", name: "4", field: "count_4", sortable: true, width: 10},
{id: "count_6", name: "6", field: "count_6", sortable: true, width: 10},
{id: "count_7", name: "7", field: "count_7", sortable: true, width: 10},
{id: "count_8", name: "8", field: "count_8", sortable: true, width: 10},
{id: "count_total", name: "Total", field: "count_total", sortable: true},
{id: "pos_perc", name: "%", field: "pos_perc", sortable: true},
{id: "decile", name: "Decile", field: "decile", sortable: true},
];
var options = {
enableCellNavigation: true,
enableColumnReorder: true,
multiColumnSort: true
};
//get default grid data (all)
var grid_data = [{'village':0, 'setting':0, 'hood':0, 'timespan':0, 'count_0':0, 'count_1':0, 'count_2':0, 'count_3':0, 'count_4':0, 'count_6':0, 'count_7':0, 'count_8':0, 'count_total':0, 'pos_perc':0, 'decile':0}];
//create the grid instance
this_grid = new Slick.Grid("#data_table_container", grid_data, columns, options);
update_grid_data();
}
update_grid_data = function(){
var settingID = $('#settingID').val();
var villageID = $('#villageID').val();
var hoodID = $('#hoodID').val();
//init the grid
$.ajax({
type: "POST",
url: '<cfoutput>#APPLICATION.site_prefix#</cfoutput>/_global/ajax/ajax_handlers.cfm',
data: {'action': 'get_qol_report_data', 'villageID': villageID, 'settingID': settingID, 'hoodID': hoodID, 'itemID': 0, 'categoryID': 0},
dataType: 'json',
success: function(data) {
push_data_to_grid(data);
}
});
}
push_data_to_grid = function(data){
this_grid.setData(data);
this_grid.render();
}
//execute the grid init
init_grid();
});
I've faced the same problem. Please, try the below code.
function updateGridView(){
data_view.beginUpdate();
data_view.setItems(update_data);
data_view.endUpdate();
data_view.refresh();
grid.invalidate();
}
function grid_refresh(){
$.ajax("<cfoutput>#APPLICATION.site_prefix#</cfoutput>/_global/ajax/ajax_handlers.cfm",{
dataType : "json",
complete: function(xhr){
update_data = eval(xhr.responseText);
updateGridView();
}
})
}
Just call the grid_refresh() function.
I implemented something like this myself and here is how I have done it. I do use the dataview which will be wiped out every times and also the grid object which will be overwritten. I am not using your code, but instead I will show you the template which I use, I actually call the same function for loading & reloading but just make sure to empty() out the grid before you reload, see 1st line of code:
<div id="myGrid" style="width:100%;height:680px;"></div>
Then I made myself a button with an onclick event that looks something like this onclick=populateMyGrid() as a refresh button (it's actually a reload icon to make it nicer) and that event will call my function to reload the data through the $.getJSON() jQuery function, see the following code:
// Display some Market Indexes on a bar on top of the Grid
function populateMyGrid() {
// empty out the Grid before refreshing the data
$('#myGrid').empty();
// columns & options definition....
columns = [
{ id: "village", ............
];
options = {
enableCellNavigation: true,
editable: true,
............
};
ajaxURL = 'myPhpAjaxFileToPullData.php?action=getdata';
$.getJSON(ajaxURL, function (ServerResponse) {
dataView = new Slick.Data.DataView();
grid = new Slick.Grid('#myGrid', dataView, columns, options);
............
// initialize the model after all the events have been hooked up
dataView.beginUpdate();
dataView.setItems(ServerResponse.data);
dataView.endUpdate();
// Refresh the data render, if user only clicked on the refresh button instead of refreshing the whole page from browser
grid.updateRowCount();
grid.render();
}); // end of getJSON
} // end of populateMyGrid
From this code, the important part of it is to empty out the grid at first and then the last 2 rows of code for refreshing your grid with new data and make sure to re-render at last. That is the way I have it working, works like a charm...oh and I also display a text showing last refresh date+time, so it's more obvious to the user of how old the data is!
Even though it's not your code sample, you should get the idea...hope it helps :)
Also if you want to repopulate the grid with some kind of filtering you send the filtering via the ajaxURL of the $.getJSON or you could also replace it with a $.post and send it via the data property as your started, if you do it that way then move all your code into the success function (or a function call). Here is a possible solution for replacing the $.getJSON call... but please note that I did not try it but it should work:
//init the grid
$.ajax({
type: "POST",
url: '<cfoutput>#APPLICATION.site_prefix#</cfoutput>/_global/ajax/ajax_handlers.cfm',
data: {'action': 'get_qol_report_data', 'villageID': villageID, 'settingID': settingID, 'hoodID': hoodID, 'itemID': 0, 'categoryID': 0},
dataType: 'json',
success : getData
});
function getData() {
dataView = new Slick.Data.DataView();
grid = new Slick.Grid('#myGrid', dataView, columns, options);
............
// initialize the model after all the events have been hooked up
dataView.beginUpdate();
dataView.setItems(ServerResponse.data);
dataView.endUpdate();
// Refresh the data render, if user only clicked on the refresh button instead of refreshing the whole page from browser
grid.updateRowCount();
grid.render();
}

Sench Touch 2 get container data /store dynamically

I want to get container data dynamically. Can any one help me to display the container items dynamically. The date or store url may be some external page, I want to get the container store/date using MVC. Can any help me?
Below is my code
Ext.define('Mvcapp.view.LayoutList',{
extend: 'Ext.Container',
xtype: 'layoutlist',
config:{
title: 'Layout',
iconCls:'star',
styleHtmlContent: true,
items:[{
data: [{
fname: 'Stratton',
lname: 'Sclavos',
role: 'Executive Chairman'
}, {
fname: 'Michael',
lname: 'Mullany',
role: 'CEO'
}, {
fname: 'Ted',
lname: 'Driscoll',
role: 'Vice President Worldwide Sales'
}, {
fname: 'Abraham',
lname: 'Elias',
role: 'Chief Technical Officer'
}, {
fname: 'Jeff',
lname: 'Hartley',
role: 'Vice President of Services and Training'
}, {
fname: 'Adam',
lname: 'Mishcon',
role: 'Vice President of Operations'
}, {
fname: 'Judy',
lname: 'Lin',
role: 'Vice President of Engineering'
}], // data
tpl: '<tpl for="."><div style="float:left;width:300px;"><strong>{lname}</strong>, {fname} <em class="muted">({role})</em></div></tpl>'
}]
}
});
Things you need to do is, first define a model having required fields. Next create a store and set model config to previously defined. While defining the store you need to choose from various proxies sencha touch has give. If you are building app that is to be built for mobile phones then there's not much choice to make. You simply have to use JsonP proxy.
Here's what I'd do -
var listitem=Ext.define('ListItem', {
extend: 'Ext.data.Model',
config: {
fields: ['fname','lname','role']
}
});
var store = Ext.create('Ext.data.Store', {
model: listitem,
autoLoad: true,
proxy: {
type: 'jsonp',
url: 'http://localhost/json_feed_url.php',
reader: {
type: 'json',
rootProperty: 'data'
}
}
});
var myList = Ext.create('Ext.List', {
styleHtmlContent:true,
store:store,
itemTpl:['<div style="float:left;width:300px;"><strong>{lname}</strong>, {fname} <em class="muted">({role})</em></div>']
});
Ext.Viewport.add(myList);
As you can see, i first defined model for our store. Store is created using JsonP proxy and it is set to load automatically. I've also set up reader, that will read the response received from server and parse it. I don't have to worry about it anymore now because all I've to do it just set the rootPropery.
Next a list is created and previously defined store is assigned in it's config. So whenever this piece of code is run, store will get data from server and display it in list. Guess that's what you want.
To start, just put this piece inside launch method of your app.js and you're good to go. In case you want php code here's it -
<?php header('Content-type:application/javascript');
$items =array();
$items[] = array('fname'=>'A','lname'=>'B','role'=>1);
$items[] = array('fname'=>'C','lname'=>'D','role'=>2);
$items[] = array('fname'=>'E','lname'=>'F','role'=>3);
$items[] = array('fname'=>'G','lname'=>'H','role'=>4);
$items[] = array('fname'=>'I','lname'=>'J','role'=>5);
print $_GET['callback'].'('.json_encode(array('data'=>$items)) .')';
?>
I'm familiar with php n all, but you can use whatever suits you. Idea is same. :D

Categories