I see what you mean and that is the expected behavior from render callback.
column.render callback purpose is to provide a cell view of the underlying data and it's called whenever the cell is rendered in grid. In non virtual mode all the cells on a page are rendered at once while in virtual mode, the cells are rendered depending upon the size of viewport ( scrollable area ).
So if you are looking for a way to persist the calculations in data (and/or) post it to the server, please use beforeValidate or change event by iterating over the changed rows and update Total Cost in newRow.
To refresh row whenever cell is changed.
cellSave: function(evt, ui){
//debugger;
this.refreshRow(ui);
},
and to update the Total cost column.
grid.on('change', function(evt, ui){
//debugger;
var rowList = ui.rowList;
for(var i=0;i<rowList.length;i++){
var ro = rowList[i],
rowData = ro.rowData,
QuantityPerUnit = rowData.UnitPrice * rowData.UnitsInStock;
rowData.QuantityPerUnit = isNaN(QuantityPerUnit)? undefined: QuantityPerUnit;
}
});
Final code:
$(function () {
var obj = {
hwrap: false,
resizable: true,
rowBorders: false,
numberCell: { show: true },
trackModel: { on: true }, //to turn on the track changes.
toolbar: {
items: [
{ type: 'button', icon: 'ui-icon-plus', label: 'New Product', listener:
{ "click": function (evt, ui) {
//append empty row at the end.
var rowData = { ProductName: 'new product', UnitPrice: 0.2 }; //empty row
var rowIndx = grid.addRow({ rowData: rowData });
grid.goToPage({ rowIndx: rowIndx });
grid.editFirstCellInRow({ rowIndx: rowIndx });
}
}
},
{ type: 'separator' },
{ type: 'button', icon: 'ui-icon-arrowreturn-1-s', label: 'Undo', cls: 'changes', listener:
{ "click": function (evt, ui) {
grid.history({ method: 'undo' });
}
},
options: { disabled: true }
},
{ type: 'button', icon: 'ui-icon-arrowrefresh-1-s', label: 'Redo', listener:
{ "click": function (evt, ui) {
grid.history({ method: 'redo' });
}
},
options: { disabled: true }
},
{
type: "<span class='saving'>Saving...</span>"
}
]
},
scrollModel: {
autoFit: true
},
historyModel: {
checkEditableAdd: true
},
editor: {
select: true
},
title: "<b>Auto save</b>",
change: function (evt, ui) {
//debugger;
var grid = this;
if (grid.isDirty() && grid.isValidChange({allowInvalid: true}).valid) {
//grid.refresh();
var changes = grid.getChanges({ format: 'byVal' });
$.ajax({
url: '/pro/products/batch',
data: {
list: JSON.stringify({
updateList: changes.updateList,
addList: changes.addList,
deleteList: changes.deleteList
})
},
dataType: "json",
type: "POST",
async: true,
beforeSend: function (jqXHR, settings) {
$(".saving", grid.widget()).show();
},
success: function (changes) {
//commit the changes.
grid.commit({ type: 'add', rows: changes.addList });
grid.commit({ type: 'update', rows: changes.updateList });
grid.commit({ type: 'delete', rows: changes.deleteList });
},
complete: function () {
$(".saving", grid.widget()).hide();
}
});
}
},
history: function (evt, ui) {
var $grid = this.widget();
if (ui.canUndo != null) {
$("button.changes", $grid).button("option", { disabled: !ui.canUndo });
}
if (ui.canRedo != null) {
$("button:contains('Redo')", $grid).button("option", "disabled", !ui.canRedo);
}
$("button:contains('Undo')", $grid).button("option", { label: 'Undo (' + ui.num_undo + ')' });
$("button:contains('Redo')", $grid).button("option", { label: 'Redo (' + ui.num_redo + ')' });
},
colModel: [
{ title: "Product ID", dataType: "integer", dataIndx: "ProductID", editable: false, width: 80 },
{ title: "Product Name", width: 165, dataType: "string", dataIndx: "ProductName",
validations: [
{ type: 'minLen', value: 1, msg: "Required" },
{ type: 'maxLen', value: 40, msg: "length should be <= 40" }
]
},
{ title: "Total Cost", hidden: false, width: 140, dataType: "string", align: "right", dataIndx: "QuantityPerUnit",
render:function(ui){
ui.rowData.QuantityPerUnit = ui.rowData.UnitPrice * ui.rowData.UnitsInStock;
ui.cellData = ui.rowData.QuantityPerUnit;
return ui.cellData;
}
},
{ title: "Unit Price", width: 100, dataType: "float", align: "right", dataIndx: "UnitPrice",
validations: [
{ type: 'nonEmpty', msg: "Required" },
{ type: 'gt', value: 0.5, msg: "better be > 0.5", warn: true}],
render: function (ui) {
var cellData = ui.cellData;
if (cellData != null) {
return "$" + parseFloat(ui.cellData).toFixed(2);
}
else {
return "";
}
}
},
{ title: "Units In Stock", width: 100, dataType: "integer", align: "right", dataIndx: "UnitsInStock",
validations: [{ type: 'gte', value: 0, msg: "Required"}]
},
{ title: "Discontinued", width: 100, dataType: "bool", align: "center", dataIndx: "Discontinued",
editor: false,
type: 'checkbox',
validations: [{ type: 'nonEmpty', msg: "Required"}]
},
{ title: "", editable: false, minWidth: 85, sortable: false,
render: function (ui) {
return "<button type='button' class='delete_btn'>Delete</button>";
},
postRender: function (ui) {
var grid = this,
$cell = grid.getCell(ui);
$cell.find(".delete_btn")
.button({ icons: { primary: 'ui-icon-scissors'} })
.bind("click", function (evt) {
grid.deleteRow({ rowIndx: ui.rowIndx });
});
}
}
],
postRenderInterval: -1, //synchronous post render.
pageModel: { type: "local", rPP: 20 },
virtualX:true,virtualY:true,
dataModel: {
dataType: "JSON",
location: "remote",
recIndx: "ProductID",
url: "/pro/products/get", //for ASP.NET
//url: "/pro/products.php", //for PHP
getData: function (response) {
return { data: response.data };
}
},
cellSave: function(evt, ui){
//debugger;
this.refreshRow(ui);
},
load: function (evt, ui) {
var grid = this,
data = grid.option('dataModel').data;
grid.widget().pqTooltip(); //attach a tooltip.
//validate the whole data.
grid.isValid({ data: data });
}
};
var grid = pq.grid("#grid_editing", obj);
/*use either beforeValidate or change event.
grid.on('beforeValidate', function(evt, ui){
//debugger;
var rowList = ui.rowList;
for(var i=0;i<rowList.length;i++){
var ro = rowList[i],
rowData = ro.rowData,
newRow = ro.newRow,
oldRow = ro.oldRow,
UnitPrice = newRow.UnitPrice === undefined? rowData.UnitPrice: newRow.UnitPrice,
UnitsInStock = newRow.UnitsInStock === undefined? rowData.UnitsInStock: newRow.UnitsInStock,
QuantityPerUnit = UnitPrice * UnitsInStock;
newRow.QuantityPerUnit = isNaN(QuantityPerUnit)? undefined: QuantityPerUnit;
oldRow.QuantityPerUnit = rowData.QuantityPerUnit;
}
});*/
grid.on('change', function(evt, ui){
//debugger;
var rowList = ui.rowList;
for(var i=0;i<rowList.length;i++){
var ro = rowList[i],
rowData = ro.rowData,
QuantityPerUnit = rowData.UnitPrice * rowData.UnitsInStock;
rowData.QuantityPerUnit = isNaN(QuantityPerUnit)? undefined: QuantityPerUnit;
}
});
});