JQGrid Programmatically Select Grid Row - jqgrid

I have a JQGrid with loadonce:true(so it's all client side) and paging enabled(with, say 20 pages).
I would like to specify a row(programmatically, without user input) and have my grid navigate to the corresponding page to select the specified row.
Is this possible with the current JQGrid?
I've looked into search and filter, but that just reloads the grid with new rows - I need my grid to navigate to the correct page - Keeping its data and structure.
I'm in the process of optimizing my grid structure, so any changes needed(say client side to server side) would be possible.

Because you use loadonce:true, then you prepare the data on the server. On the server side you can decide which row must be selected. On the server side you can also easy calculate on which page will be the selected row. The id of selected row and the selected page you can for example include as a part of the userdata. So the data sent from the server could looks like following:
{
"total": 5,
"page": 1,
"records": 107,
"rows": [
...
],
"userdata": {
"page": 3,
"selId": 24
}
}
Inside of loadComplete you can do about following
loadComplete: function(data) {
if (jQuery("#list").getGridParam('datatype') === "json") {
// data.userdata is the same as jQuery("#list").getGridParam('userData');
var userdata = jQuery("#list").getGridParam('userData');
var curPage = jQuery("#list").getGridParam('page'); // is always 1
if (curPage !== userdata.page) {
setTimeout(function(){
jQuery("#list").setGridParam(
{ page: userdata.page }).trigger("reloadGrid");
jQuery("#list").setSelection (userdata.selId, true);
},100);
}
else {
jQuery("#list").setSelection (userdata.selId, true);
}
}
}
A working examples you can see on http://www.ok-soft-gmbh.com/jqGrid/DataToSelect.htm and http://www.ok-soft-gmbh.com/jqGrid/DataToMultiSelect.htm.
UPDATE: Free jqGrid supports multiPageSelection:true option strarting with the version 4.10.0. The option allows to set selection of multiple rows in the grid very easy (and it works very quickly, because it set selection state directly during creating the body of the grid). See the answer and the demo and the readme to 4.10.0.

Related

How to avoid multiple radio buttons selection when paginating a data table

I'm having a problem here:
When I select a radion button in a page 1 and another one in the same page it works fine, but when I select a radio button in the page number 2, and go back to the page number 1, that first radio button still selected and the one in the page number 2 is also selected, how can I solve this problem? I was looking for a solution here in the forum but couldn't find someone with the same problem.
*[code] $(document).ready(function () { var oTable = $('#example').dataTable({ "bJQueryUI": true, "sPaginationType": "full_numbers" }); }); [/code]*
There is no solution from DataTables itself. You'll have to do it on your own.
What you can do is save the selected value yourself on a click. Imagine you have a table called projectsTable. Then you can write:
var selection = -1;
$("#projectsTable input[type=radio]").on
(
"click",
function()
{
selection = $(this).val();
}
);
Then, you'll have to unselect all the wrong values every time the page changes. Add a callback in the construction of your table for page draw to do that:
//other settings go here, like the dom one
//you can choose your own, no need for this specific one
"dom": "lfBrtip",
"fnDrawCallback":
function(oSettings)
{
$("#projectsTable input[type=radio][value!="+selection+"]").prop('checked', false);
}
Every time the page is changed, this piece of code deselects everything you don't want to be selected any longer.

JqGrid Performance - setRowData lags

setRowData takes almost 5 seconds when the grid has 300 rows, 30 cols, and 4 frozen cols.
$("#tbl").jqGrid({
gridview: true,
url: '../controller/GetData',
datatype: "json",
rowNum: pageSize,
viewrecords: true,
loadtext: '',
loadui: 'disable',
rowList: [1000, 2000, 3000],
width: $(window).width() - LeftMargin,
shrinkToFit: false,
pager: '#dvPager',
pagerpos: 'left',
colNames: GetColNames(selectedViewName, viewCols),
colModel: GetColModel(selectedViewName, viewCols),
cmTemplate: {
title: false
},
recordtext: "Displaying {0} - {1} of {2}",
rowattr: function (rowData) {
},
//onCellSelect: function (rowid, iCol, cellcontent, e) {
// e.stopPropagation();
// proto.editcell(rowid, iCol, cellcontent, e);
//},
loadComplete: function (data) {..},
onPaging: function (data) {..},
serializeGridData: function (postData) {..}
});
Can you please guide me with the performance tuning tips?
In GetColModel we are binding columns as follows -
'Discount': {
name: 'Discount', index: 'Discount', width: colModelWidthDict['Discount'], title: false, sortable: false, resizable: colResizable, hidden: !bReadCalculationSellInfo,
formatter: function (cellValue, option, rowObject) {
if (rowObject.level == 0 || (rowObject.Flag == 0 && (rowObject.Discountable && rowObject.Discountable.toLowerCase() == 'no')))
return '';
else {
if (!proto.IsReadOnly(rowObject) && ((rowObject.Flag == 0 && (rowObject.Discountable && rowObject.Discountable.toLowerCase() == 'yes')) || rowObject.Flag > 0))
return proto.GetControls("Discount", option.rowId, "Numeric", rowObject.Discount, 6, 90)
else
return '<span class="amt">' + cellValue + '</span>';
}
},
unformat: function (cellValue, option) {
return cellValue;
},
cellattr: function (rowId, val, rowObject, cm, rdata) {
if (parseInt(rowObject.PPPChngDisc) == 1)
return ' style="color:red"';
}
}
//code for colModel - 1 column above
In general you should prefer to modify only small number of rows. If you modify one row then the web browser have to recalculate position of all other rows (500 rows) of the grid. In the case modifying 300 rows means 300*500 operations for the web browser.
If you fill the whole grid and you use gridview: true option then jqGrid places at the beginning all the data for the grid body in array of strings. After preparing of all information in string format jqGrid place the whole body of the grid as one operation. It's just one assigning innerHTML property of tbody. Thus it's more effective to reload the whole grid as to make modification of many rows of the grid in the loop using setRowData.
So I would recommend you to reload the grid in your case.
Moreover you should consider to reduce the number of rows in the page of grid. If you use rowNum: 500 you just reduce the performance of your page tin many times without any advantage for the user. The user can see about 20 rows of the data only. Only if the user scrolls he/she is able to see another information. In the same way the user could click next page button. Holding 500 rows force web browser make a lot of unneeded work. Modifying of the first row from 500 the web browser have to recalculate position of all 500 rows. Even if the user move the mouse over a row the web browser change the class of the hovering row and it have to recalculate position of all other from 500 rows event the rows are not visible.
One more remark. The implementation of frozen columns in jqGrid 4.6 is slow. Starting with the end of 2014 and publishing 4.7.1 with changing Lisanse and making jqGrid commercial I develop free jqGrid as fork based on jqGrid 4.7 (see the readme for more details). I implemented a lot of changes and new features in the fork. The recent changes includes many improvement of frozen columns feature. If you loads free jqGrid or just try it directly from GitHub by including the corresponding URLs (see the wiki) you will see that the performance of frozen columns is dramatically improved. Additionally frozen columns supports now row and cell editing in all editing modes. I plan to publish free jqGrid 4.9 soon. So I recommend you to try it.
UPDATED: You posted the code, which you use in comments below. I see the main problem in the implementation of Process function which will be called on change in the <input> fields on the grid. I recommend you to try something like this one:
function Process(contrl) {
var $cntrl = $(contrl),
$td = $cntrl.closest("tr.jqgrow>td"),
$tr = $td.parent(),
rowId = $tr.attr("id"),
newValue = $cntrl.val(),
$grid = $("#list"),
p = $grid[0].p,
colModel = p.colModel,
columnName = colModel[$td[0].cellIndex].name, // name of column of edited cell
localRowData = $grid.jqGrid("getLocalRow", rowId),
iColTotal = p.iColByName.total;
// modify local data
localRowData[columnName] = newValue;
localRowData.total = parseInt(localRowData.quantity) * parseFloat(localRowData.unit);
// modify the cell in the cell (in DOM)
//$grid.jqGrid("setCell", rowId, "total", localRowData.total);
$($tr[0].cells[iColTotal]).text(localRowData.total);
return false;
}
One can event improve the code and not use setCell at all. It's important to understand that local grid hold the data in the form of JavaScript object. The access to the data is very quickly. You can modify the data without any side effects existing with DOM elements. In the above code I use only relative search inside of the DOM of the row using closest and parent. It works very quickly. I recommend to save $("#list") in the variable outside of the Process (move var $grid = $("#list") in the outer function) and just use $grid variable. It will make access to the DOM of page very quickly. The line localRowData = $grid.jqGrid("getLocalRow", rowId) get the reference to internal JavaScript data object which represent the internal data of the row. By modifying the data by localRowData[columnName] = newValue; or localRowData.total = ... we make the half of job. One need only to change the value in the DOM on the cell Total. I used iColByName which exist in the latest sources of free jqGrid on GitHub. It get just the column index in colModel by column name. One can find the same information alternatively if one use more old jqGrid. The line $($tr[0].cells[iColTotal]).text(localRowData.total); shows how to assign manually the data in the cell. It's the most quick way to do this, but one assign the data without the usage formatter. For more common case (for example in case of frozen "total" column) one can use the commented row above: $grid.jqGrid("setCell", rowId, "total", localRowData.total);.

Slickgrid - populate new row immediately on display

I am making an editable slickgrid with enableAddNewRow: true.
I want each new row to have default data, as soon as it shows up.
The suggested solution in example 4 is to use the onAddNewRow event:
grid.onAddNewRow.subscribe(function (e, args) {
var item = {"num": data.length, "id": "new_" + (Math.round(Math.random() * 10000)), "title": "New task", "duration": "1 day", "percentComplete": 0, "start": "01/01/2009", "finish": "01/01/2009", "effortDriven": false};
$.extend(item, args.item);
dataView.addItem(item);
});
This is not quite what I want however, because the onAddNewRow event does not fire until you put a value in one of the cells (ie. you have to type something in one of the cells and then tab/click away from the cell, before the default values are shown in the row).
What I need is a way to insert the default values as soon as the new line shows up in the grid.
Anyone have a suggestion?
Edit
So, on further experimentation, I have come up with the following:
grid.onBeforeEditCell.subscribe(function(e, args) {
if (typeof args.item === 'undefined) {
grid.onAddNewRow.notify({}, null, null);
}
})
which adds the new row with the default data, as soon as the first cell in a new row is accessed.
My problem now is, that the cell editor is opened before the new row is completely added, meaning, that the editor does not pick up the default value, since it was still undefined at the time of initialization of the editor.
Since your problem seems like timing issue, it would be recommended to use a timeout (jQuery or raw javascript). I mention jQuery since first of all SlickGrid runs on top of jQuery but also because quite often with jQuery we need to insert some timeout() especially for Firefox as it renders differently. That being said, you could try to use the jQuery timeout() function as this:
setTimeout(function() {
// Do something after 1 second...
}, 1000);

How to prompt user that edits have been made upon changing pages or sorting in Kendo Grid

I have run into an issue with the kendo-ui grid that I can't solve. In my application I am using kendo-grid in batch edit mode to allow a data entry person to quickly make edits to several records on a screen. This particular grid is set to allow paging and sorting.
I am looking for a way to prompt the user upon attempting a sort or clicking on one of the paging links while there have been edits made on the particular page (as in page of the datagrid/datasource). If the user clicks OK, then I want to continue onto the next page of data, otherwise I would like to cancel the edit and keep the user on the current page with the existing edits. My other option would simply be to automatically commit any changes upon paging or sorting or allow them to cancel and stay on the current page.
My attempts thus far have been to use the change event on the actual grid datasource to store a dirty flag into an observable and then try to catch the actual sorting or paging change by listening to the dataBinding event of the grid and display the prompt when my isDirty flag is true and the e.action of the dataBinding event == "rebind".
The problem that I am having with this approach is that the dataBinding event fires after the grid dataSource has already performed a fetch of another page and not prior. This prevents me from both saving any edits or even maintaining existing edits.
I can't find any event that I can subscribe to that will allow me to do inspections prior to a page fetch/sort. Has anyone else come up with a way to handle this scenario? It seems to me that it would be a pretty common thing that people would want to handle in data entry applications.
I solved my problem by subscribing to the requestStart event of the dataSource. So now my Kendo Datasource looks like this:
{
pageSize: 15,
batch: false,
schema: {
model: {
id: "LocalId",
fields: {
LocalId:
{
editable: false,
nullable: true
},
Ssn: {
validation: {
required: true
}
},
QtrEarnings: {
type: "number",
defaultValue: 0,
validation: { required: true, min: 0 }
}
}
}
},
change: function (e) {
onDataChange(e);
},
requestStart: function (e) {
if (homeModel.get("dataSource").hasChanges()) {
if (confirm("You have made edits to data! Click Cancel to stay on this page or click Ok to abandon your changes and continue.") == false) {
e.preventDefault();
}
}
}
}

Using jQGrid and jQUery tabs how to oprimize the number of grids

I have 3 different tabs where i am displaying data using jQGrids(each tab contain one grid).
But i just thought that my grids are completely the same, only the difference is that they using different url to get data.
So I have three similar girds on each tab only with different urls:
First: url: '/Home/GetData?id=1' Second: url: '/Home/GetData?id=2' and Third: url: '/Home/GetData?id=3'
So i was thinking that may be i may declare grid only once and than on each tab click a can pass the url to load data? So on each tab click jQGrid will be populating from the new url.
May be some one may have any ideas about that?
Or may be some one may have better ideas how to reduce "jQGrid copy-paste" in that case?
UPDATE 0:
Nearly get it work i mean it is working but there is one small problem,
When i am switching tabs the header of the grid getting lost...and some jqgrid formatting as well.
here is my code:
$("#tabs").tabs({
show: function (event, ui) {
if (ui.index == 0) {
//$("#Grid1").appendTo("#tab1");
//$("#Grid1Pager").appendTo("#tab1");
//When Appending only pager and grid div, header getting lost so i've append the whole grid html instead
$("#gbox_Grid1").appendTo("#tab1");
changeGrid("#Grid1", 1);
}
else if (ui.index == 1) {
//$("#Grid1").appendTo("#tab2");
//$("#Grid1Pager").appendTo("#tab2");
$("#gbox_Grid1").appendTo("#tab2");
changeGrid("#Grid1", 2);
}
else if (ui.index == 2) {
//$("#Grid1").appendTo("#tab3");
//$("#Grid1Pager").appendTo("#tab3");
$("#gbox_Grid1").appendTo("#tab3");
changeGrid("#Grid1", 3);
}
}
});
function changeGrid(grid, id) {
$(grid).jqGrid('setGridParam', {
url: '/Home/GetData?id=' + id
});
$(grid).trigger('reloadGrid');
}
UPDATE 1
All right, i've changed the code to append the whole grid instead of appending grid div and pager only. So it is working like that.
You can basically make the tabs as regular buttons that will call some function which sets new URL parameter to the grid and reloads it.
The function should be something like this:
function changeGrid(grid, id) {
$(grid).jqGrid('setGridParam', {
url: '/Home/GetData?id=' + id, page: 1
});
$(grid).trigger('reloadGrid');
}
Note that I set the page to 1. Keep in mind that you might need to set the default sorting column or something similar depending on your solution.
UPDATE
If you really want to go with the tabs, the 'show' event handler can be simplified.
Try this:
$("#tabs").tabs({
show: function (event, ui) {
var id = ui.index + 1;
$("#gbox_Grid1").appendTo("#tab" + id);
$("#Grid1").jqGrid('setGridParam', {
url: '/Home/GetData?id=' + id
});
$("#Grid1").trigger('reloadGrid');
}
});

Resources