I have developed a master/detail grid solution using Handsontable. I am using the afterSelectionEnd event to capture and evaluate a row change by the user in the master grid. When the row changes, I have functions to evaluate the content of a "flag" field to determine whether there are detail records, and if there are detail records, to initialize and load the detail data into a detail grid (so I'm initializing and loading one hot grid from inside another).
When I step through my code slowly in Firefox debugger, the master and detail grids load and save correctly, but when I remove breakpoints and run the code at speed, I almost always get a failure to load data into the grid and/or to save the data in the grid.
The back end of the grid data load and save functions is via a web service call launched by Ajax in the client.
I'm not sure whether the creation of one grid from inside an existing grid could be the problem, or perhaps it simply a matter of synchronicity ... the data is not returning fast enough to load into the grid or not loading fast enough to use in the save function.
Has anyone tried anything like this or run into similar issues, or have a better idea of how I might debug this further? I sense that perhaps I'm not using the concept of a callback correctly, but not 100% sure, since the code does execute successfully when slowed down.
Here's the code:
@section HandsOnTable{
<script type="text/javascript" src="~/Scripts/jquery-2.1.4.js"></script>
<script type="text/javascript" src="~/Scripts/handsontable.full.min.js"></script>
<link rel="stylesheet" media="screen" href="~/Content/handsontable.full.min.css">
<style>
#hot .handsontable table thead th{
background-color: #4A7845;
color: #ffffff;
font-weight:bold;
font-size:11px;
vertical-align:bottom;
}
#emp .handsontable table thead th{
background-color: #669262;
color:#ffffff;
font-weight:bold;
font-size:11px;
vertical-align:bottom;
}
#emp .handsontable table tbody tr th {
background-color: #669262;
color: #ffffff;
font-weight:bold;
font-size:11px;
vertical-align:bottom;
}
#vendor .handsontable table thead th{
background-color: #8CAF88;
color: #ffffff;
font-weight:bold;
font-size:11px;
vertical-align:bottom;
}
#vendor .handsontable table tbody tr th{
background-color: #8CAF88;
color: #ffffff;
font-weight:bold;
font-size:11px;
vertical-align:bottom;
}
</style>
<script type="text/javascript">
//GRID MANAGEMENT FUNCTIONS
//I load a grid with data.
function initData(obj, lineNum) {
var ServiceURL = location.href.substring(0, location.href.lastIndexOf("/") + 1) + "JeTransaction/InitGridData";
//alert(ServiceURL);
lineNum = lineNum === undefined ? 0 : lineNum;
//alert("we are in the initData function.");
$.ajax({
url: ServiceURL,
type: "POST",
data: "objId=" + obj + "&objLineNum=" + lineNum,
dataType: "json",
cache: false,
success: function (result) {
//alert("This is initData result: " + result);
$(obj).handsontable('loadData', result);
$("#status").html("<p>" + obj + " Grid initialized.</p>");
},
error: function (xhr, textstatus, errorthrown) {
$("#status").html("<p>Error in initData: " + obj + ": " + textstatus + " - " + errorthrown + " - " + xhr + "</p>");
}
});
}
//I save all rows in the grid.
function saveGridData(obj) {
var ServiceURL = location.href.substring(0, location.href.lastIndexOf("/") + 1) + "JeTransaction/SaveGridData";
var formdata = $(obj).handsontable('getData');
$.ajax({
url: ServiceURL,
type: "POST",
dataType: "json",
cache: false,
data: "objID=" + obj + "&gridContent=" + JSON.stringify(formdata),
success: function (result) {
$("#status").html("<p>" + obj + " data was successfully saved.</p>");
},
error: function (xhr, textstatus, errorthrown) {
$("#status").html("<p>Error in saveGridData: " + obj + ": " + textstatus + " - " + errorthrown + " - " + xhr + "</p>");
}
});
}//end of saveGridDataToFile
/////I save a single row of data in a grid
//function saveRowData(obj, row) {
// var ServiceURL = location.href.substring(0, location.href.lastIndexOf("/") + 1) + "JeTransaction/SaveRowData";
// var formdata = $(obj).handsontable('getSourceDataAtRow', row);
// $.ajax({
// url: ServiceURL,
// type: "POST",
// dataType: "json",
// cache: false,
// data: "objId=" + obj + "&formdata="+ formdata,
// success: function (result) {
// $("#status").html("<p>Data was successfully saved.</p>");
// },
// error: function (xhr, textstatus, errorthrown) {
// alert("This is textstatus for saveRowData: " + textstatus);
// alert("This is the error thrown for saveRowData: " + errorthrown);
// }
// });
//}
//I delete a single row of data.
function deleteRowData(PkJeUpload) {
var ServiceURL = location.href.substring(0, location.href.lastIndexOf("/") + 1) + "JeTransaction/DeleteRowData";
$.ajax({
url: ServiceURL,
type: "POST",
dataType: "json",
cache: false,
data: "PkJeUpload=" + PkJeUpload,
success: function (result) {
$("#status").html("<p>Row data was successfully removed.</p>");
},
error: function (xhr, textstatus, errorthrown) {
$("#status").html("<p>Error in deleteRowData: " + PkJeUpload + ": " + textstatus + " - " + errorthrown + " - " + xhr + "</p>");
}
})
}
//I load a grid data with a default row
function createDefaultGridRow(obj, row, newRowData) {
switch (obj) {
case "#hot":
$(obj).handsontable('setDataAtRowProp', row, 'lineNum', newRowData.newLine + 1);
$(obj).handsontable('setDataAtRowProp', row, 'RT', 'L');
$(obj).handsontable('setDataAtRowProp', row, 'txnAmount', '0.00');
$(obj).handsontable('setDataAtRowProp', row, 'glAccount', '');
$(obj).handsontable('setDataAtRowProp', row, 'org', '');
$(obj).handsontable('setDataAtRowProp', row, 'txnDescr', '');
$(obj).handsontable('setDataAtRowProp', row, 'projNumber', '');
$(obj).handsontable('setDataAtRowProp', row, 'notes', '');
$(obj).handsontable('setDataAtRowProp', row, 'empLabor', 'N');
$(obj).handsontable('setDataAtRowProp', row, 'vendorLabor', 'N');
$(obj).handsontable('setDataAtRowProp', row, 'pkJeUpload', '0');
$(obj).handsontable('setDataAtRowProp', row, 'jeInternalCode', newRowData.newJeInternalCode);
$(obj).handsontable('setDataAtRowProp', row, 'bFlag', newRowData.newBillFlag);
$(obj).handsontable('setDataAtRowProp', row, 'rFlag', newRowData.newRevFlag);
$(obj).handsontable('setDataAtRowProp', row, 'hdrDescr', newRowData.newTxnHeader);
break;
case "#emp":
$(obj).handsontable('setDataAtRowProp', row, 'RT', 'E');
$(obj).handsontable('setDataAtRowProp', row, 'lineNum', newRowData.newLine);
$(obj).handsontable('setDataAtRowProp', row, 'SEQ', newRowData.newSEQ);
$(obj).handsontable('setDataAtRowProp', row, 'GLC', '');
$(obj).handsontable('setDataAtRowProp', row, 'PLC', '');
$(obj).handsontable('setDataAtRowProp', row, 'emplId', '');
$(obj).handsontable('setDataAtRowProp', row, 'laborHours', '0.00');
$(obj).handsontable('setDataAtRowProp', row, 'laborAmount', '0.00');
$(obj).handsontable('setDataAtRowProp', row, 'effBillDate', '');
$(obj).handsontable('setDataAtRowProp', row, 'pkJeUpload', '0');
$(obj).handsontable('setDataAtRowProp', row, 'jeInternalCode', newRowData.newJeInternalCode);
break;
case "#vendor":
$(obj).handsontable('setDataAtRowProp', row, 'RT', 'V');
$(obj).handsontable('setDataAtRowProp', row, 'lineNum', newRowData.newLine);
$(obj).handsontable('setDataAtRowProp', row, 'SEQ', newRowData.newSEQ);
$(obj).handsontable('setDataAtRowProp', row, 'GLC', '');
$(obj).handsontable('setDataAtRowProp', row, 'PLC', '');
$(obj).handsontable('setDataAtRowProp', row, 'vendorId', '');
$(obj).handsontable('setDataAtRowProp', row, 'vendorEmplId', '');
$(obj).handsontable('setDataAtRowProp', row, 'laborHours', '0.00');
$(obj).handsontable('setDataAtRowProp', row, 'laborAmount', '0.00');
$(obj).handsontable('setDataAtRowProp', row, 'effBillDate', '');
$(obj).handsontable('setDataAtRowProp', row, 'pkJeUpload', '0');
$(obj).handsontable('setDataAtRowProp', row, 'jeInternalCode', newRowData.newJeInternalCode);
break;
}
}
//I close the selected grid
function closeGrid(obj) {
$(obj).handsontable('destroy');
}
//VALIDATATION FUNCTIONS
//
//I pre-validate transactions in the grid
function validateTransactions(obj, full) {
//Users pre-validate transactions before moving to SubmitJournal entry.
alert("Attempting to validate the transactions. All rows in the grid must have a valid line number and a valid RT value.");
//Clear the current validation messages before revalidating.
removeValidationMessages(obj);
//Save grid changes. Set hotRowActiveCurrent to the last row of the grid to provide a valid value for the save function.
hotRowActiveCurrent = $(obj).handsontable('countRows');
saveGridData(obj);
var ServiceURL = location.href.substring(0, location.href.lastIndexOf("/") + 1) + "JeTransaction/ValidateJeTransactionsClientSide";
var jeInternalCode = $(obj).handsontable('getDataAtRowProp', 0, "jeInternalCode");
$.ajax({
url: ServiceURL,
type: "POST",
dataType: "json",
data: "jeInternalCode=" + jeInternalCode,
cache: false,
success: function (result) {
var txnArray = $.parseJSON(result.jsonResponse);
$(obj).handsontable('loadData', txnArray);
$("#status").text(result.validationResult);
},
error: function (xhr, textstatus, errorthrown) {
alert("This is textstatus for validateTransactions: " + textstatus);
alert("This is the error thrown for validateTransactions: " + errorthrown);
}
});
}//end of validateJeTransactions
//I remove validation messages from the grid (used when re-validating)
function removeValidationMessages(obj) {
var numberOfRows = $(obj).handsontable('countRows');
var columnId = 10;
for (var i = 0; i < numberOfRows - 1; i++) {
$(obj).handsontable('setDataAtCell', i, columnId, "");
}
$(obj).handsontable('render');
}
//I validate that if a line number is specfied, it is numeric.
function lineValidator(value, callback) {
if ($.isNumeric(value)) {
callback(true);
}
else {
alert("Line number must be numeric.");
callback(false);
}
}
//All parts of the org number must be numeric, and can be separated by decimal.
function orgNumberValidator(value, callback) {
var orgNumbers;
orgNumbers = value.split(".");
$.each(orgNumbers, function (index, value) {
if ($.isNumeric(value)) {
callback(true);
}
else {
alert("The org number parts must be numeric.");
callback(false);
}
});
}
//Txn desc must be 30 characters or less.
function txnDescValidator(value, callback) {
if (value.length > 30) {
alert("Input must be less that 30 characters.");
callback(false);
}
else {
callback(true);
}
}
//Notes field must be 250 characters or less.
function notesValidator(value, callback) {
if (value.length > 250) {
alert("Input must be less that 250 characters.");
callback(false);
}
else {
callback(true);
}
}
//MANAGE REPEATING ENTRIES
function updateRepeatingEntryColumns(obj, col) {
//If the change occurred on one of these columns, make sure that all column values match the first
//row value.
var gridInstance = obj.instance;
var newValue = gridInstance.getDataAtCell(0, col);
if (!(newValue === undefined)) {
obj.handsontable.renderers.TextRenderer.apply(this, newValue);
}
}
//I set all rows to the same value as the first row for this column.
function bFlagUpdater(value, callback) {
alert("The row is " + this.row);
if (this.row == 0) {
var rowCount = this.instance.countRows();
for (var i = 1; i < rowCount - 1; i++) {
this.instance.setDataAtCell(i, 13, value);
}
}
callback(true);
}
//I set all rows to the same value as the first row for this column.
function rFlagUpdater(value, callback) {
alert("The row is " + this.row);
if (this.row == 0) {
var rowCount = this.instance.countRows();
for (var i = 1; i < rowCount - 1; i++) {
this.instance.setDataAtCell(i, 14, value);
}
}
callback(true);
}
//I set all rows to the same value as the first row for this column.
function txnHdrUpdater(value, callback) {
alert("The row is " + this.row);
if (this.row == 0) {
var rowCount = this.instance.countRows();
for (var i = 1; i < rowCount - 1; i++) {
this.instance.setDataAtCell(i, 15, value);
}
}
callback(true);
}
//MANAGE DETAIL GRIDS
//I initiate the display/hide features of the employee labor detail records for each JE TXN line.
function empValidator(value, callback) {
if (value !== 'Y' && value !== 'N') {
alert("Y or N required.");
callback(false);
}
if (value == 'Y') {
alert("We need to instantiate a new HOT instance for the employee labor details.");
saveGridData("#hot");
var lineNum = $('#hot').handsontable('getDataAtCell', (this).row, 0) - 1;
createEmpLaborRecord(lineNum);
callback(true);
}
}
//I initiate the display/hide features of the vendor labor detail records for each JE TXN line.
function vendValidator(value, callback) {
if (value !== 'Y' && value !== 'N') {
alert("Y or N required.");
callback(false);
}
if (value == 'Y') {
alert("We need to instantiate a new HOT instance for the vendor labor details.");
saveGridData("#hot");
//var lineNum = $('#hot').handsontable('getDataAtCell', (this).row, 0) - 1;
//createVendorLaborRecord(lineNum);
callback(true);
}
}
//Check the values of the empLabor and vendLabor columns. If they are "Y", create a new
//grid and load the data
function checkForDetailData(r) {
//alert("We are going to check for detail data.");
//Check the value of the empLabor and vendLabor columns for this row.
var isEmpLabor = $("#hot").handsontable('getDataAtCell', r, 8);
var isVendLabor = $("#hot").handsontable('getDataAtCell', r, 9);
if (isEmpLabor == "Y") //EmpLabor
{
//Load EmpLabor Details for the line.
//alert("We are going to load the emp labor detail.");
//Read the existing employee labor detail into a new emp labor table.
createEmpLaborRecord(r);
}
if (isVendLabor == "Y") //VendorLabor
{
//alert("We are going to load the vendor labor detail.");
//Read the existing vendor labor detail into the vendor labor table.
createVendorLaborRecord(r);
}
return;
}
//Create and fill an employee labor detail grid
function createEmpLaborRecord(lineNum) {
var $container2 = $("#emp");
$container2.handsontable({
startRows: 1,
startCols: 1,
minRows: 0,
minCols: 0,
rowHeaders: true,
colHeaders: function (col) {
switch (col) {
case 0:
return 'RT';
case 1:
return "JE TXN#";
case 2:
return 'DETAIL<br/>RECORD ID';
case 3:
return 'GLC';
case 4:
return 'PLC';
case 5:
return 'EMPLOYEEE<br/>ID';
case 6:
return 'LABOR<br/>HOURS';
case 7:
return 'LABOR<br/>AMOUNT';
case 8:
return 'EFFECTIVE<br/>BILLING DATE';
case 9:
return 'PKJEUPLOAD';
case 10:
return 'JEINTERNALCODE';
default:
return;
}
},
columns: [
{ data: 'RT', type: 'text' },
{ data: 'lineNum', type: 'numeric', format: 0 },
{ data: 'SEQ', type: 'numeric', format: 0 },
{ data: 'GLC', type: 'text' },
{ data: 'PLC', type: 'text' },
{ data: 'emplId', type: 'text' },
{ data: 'laborHours', type: 'numeric', format: '0,0.00', language: 'en' },
{ data: 'laborAmount', type: 'numeric', format: '0,0.00', language: 'en' },
{ data: 'effBillDate', type: 'text' },
{ data: 'pkJeUpload', type: 'numeric', format: 0 },
{ data: 'jeInternalCode', type: 'text' }
],
minSpareRows: 1,
contextMenu: true,
autoWrapRow: true,
autoWrapCol: true,
beforeRemoveRow: function (index, amount) {
alert("We are in beforeRemoveRow");
for (var c = index; c < amount; c++) {
var arrayRowData = this.getDataAtRow(c);
var PkJeUpload = arrayRowData[11];
deleteRowData(PkJeUpload);
}
},
afterRemoveRow: function () {
//reload the grid with updated row numbers.
initData("#emp");
},
afterSelectionEnd: function (r, c, r2, c2) {
alert("We have triggered createEmpLaborRecord afterSelectionEnd.");
if (empRowActiveCurrent != r) {
alert("The row has changed.");
//Save the employee labor grid data
saveGridData("#emp");
//We have saved all of the current row data. Update the current row reference.
empRowActiveCurrent = r;
//Get the data from the last populated row to be carried into the new row
//Last populated row will be
var totalRows = $("#emp").handsontable('countRows');
//If the new row is empty, create the new default row
if ($("#emp").handsontable('isEmptyRow', r)) {
//The grid is configured to always have one empty row at the end. In order to get the last row with data
//we need to offset the row count by 2.
var newRowData = {
newRT: $("#emp").handsontable('getDataAtRowProp', totalRows - 2, "RT")
, newLine: $("#emp").handsontable('getDataAtRowProp', totalRows - 2, "lineNum")
, newSEQ: ($("#emp").handsontable('getDataAtRowProp', totalRows - 2, "SEQ") * 1) + 1
, newBillFlag: $("#emp").handsontable('getDataAtRowProp', totalRows - 2, "bFlag")
, newRevFlag: $("#emp").handsontable('getDataAtRowProp', totalRows - 2, "rFlag")
, newTxnHeader: $("#emp").handsontable('getDataAtRowProp', totalRows - 2, "hdrDescr")
, newJeInternalCode: $("#emp").handsontable('getDataAtRowProp', 0, "jeInternalCode")
};
createDefaultGridRow("#emp", r, newRowData);
}
}
}
});
//Load the employee grid
initData("#emp", lineNum);
//Make sure every row in the grid has the jeInternalCode from the master transaction.
var empJeInternalCode = $("#hot").handsontable("getDataAtCell", 0, 12);
$('#emp').handsontable('setDataAtCell', 0, 10, empJeInternalCode);
//Save the emp grid data
if ($("#emp").handsontable('getInstance') !== undefined) {
saveGridData("#emp");
}
return;
}
//Create and fill a vendor labor detail grid
function createVendorLaborRecord(lineNum) {
var $container2 = $("#vendor");
$container2.handsontable({
startRows: 1,
startCols: 1,
minRows: 0,
minCols: 0,
rowHeaders: true,
colHeaders: function (col) {
switch (col) {
case 0:
return 'RT';
case 1:
return "JE TXN#";
case 2:
return 'DETAIL<br/>RECORD ID';
case 3:
return 'GLC';
case 4:
return 'PLC';
case 5:
return 'VENDOR<br/>ID';
case 6:
return 'VENDOR<br/>EMPLOYEE ID';
case 7:
return 'LABOR<br/>HOURS#';
case 8:
return 'LABOR<br/>AMOUNT';
case 9:
return 'EFFECTIVE<br/>BILLING DATE';
case 10:
return 'PKJEUPLOAD';
case 11:
return 'JEINTERNALCODE';
case 12:
return 'BILL<br/>FLAG';
case 13:
return 'REV<br/>FLAG';
case 14:
return 'TXN<br/>HEADER';
default:
return;
}
},
columns: [
{ data: 'RT', type: 'text' },
{ data: 'lineNum', type: 'numeric', format: 0 },
{ data: 'SEQ', type: 'numeric', format: 0 },
{ data: 'GLC', type: 'text' },
{ data: 'PLC', type: 'text' },
{ data: 'vendorId', type: 'text' },
{ data: 'vendorEmplId', type: 'text' },
{ data: 'laborHours', type: 'numeric', format: '0,0.00', language: 'en' },
{ data: 'laborAmount', type: 'numeric', format: '0,0.00', language: 'en' },
{ data: 'effBillDate', type: 'text' },
{ data: 'pkJeUpload', type: 'numeric', format: 0 },
{ data: 'jeInternalCode', type: 'text' },
{ data: 'bFlag', type: 'text', validator: bFlagUpdater },
{ data: 'rFlag', type: 'text' },
{ data: 'hdrDescr', type: 'text' }
],
minSpareRows: 1,
contextMenu: true,
autoWrapRow: true,
autoWrapCol: true,
beforeRemoveRow: function (index, amount) {
alert("We are in beforeRemoveRow");
for (var c = index; c < amount; c++) {
var arrayRowData = this.getDataAtRow(c);
var PkJeUpload = arrayRowData[11];
deleteRowData(PkJeUpload);
}
},
afterRemoveRow: function () {
//reload the grid with updated row numbers.
initData(this);
},
afterSelectionEnd: function (r, c, r2, c2) {
console.log("Vendor after selection end.");
if (vendorRowActiveCurrent != r) {
alert("The row has changed.");
//Save the vendor grid data
if ($("#vendor").handsontable('getInstance') !== undefined) {
saveGridData("#vendor");
}
//We have saved all of the current row data. Update the current row reference.
vendorRowActiveCurrent = r;
//Collect and store the jeInternalCode from the master transaction
var vendorJeInternalCode = $("#hot").handsontable("getDataAtCell", 0, 12);
//Get the data from the last populate row to be carried into the new row
//Last populated row will be
var totalRows = $("#vendor").handsontable('countRows');
var rowIndex = 0;
//If the new row is empty, create the new default row
if ($("#vendor").handsontable('isEmptyRow', r)) {
//The grid is configured to always have one empty row at the end. In order to get the last row with data
//we need to offset the row count by 2.
var newRowData = {
newRT: $("#vendor").handsontable('getDataAtRowProp', totalRows - 2, "RT")
, newLine: $("#vendor").handsontable('getDataAtRowProp', totalRows - 2, "lineNum")
, newSEQ: ($("#vendor").handsontable('getDataAtRowProp', totalRows - 2, "SEQ") * 1) + 1
, newBillFlag: $("#vendor").handsontable('getDataAtRowProp', totalRows - 2, "bFlag")
, newRevFlag: $("#vendor").handsontable('getDataAtRowProp', totalRows - 2, "rFlag")
, newTxnHeader: $("#vendor").handsontable('getDataAtRowProp', totalRows - 2, "hdrDescr")
, newJeInternalCode: $("#vendor").handsontable('getDataAtRowProp', 0, "jeInternalCode")
};
createDefaultGridRow("#vendor", r, newRowData);
}
}
}
});
//Load the vendor grid
initData('#vendor', lineNum);
//Make sure every row in the grid has the jeInternalCode from the master transaction.
var vendorJeInternalCode = $("#hot").handsontable("getDataAtCell", 0, 12);
$('#vendor').handsontable('setDataAtCell', 0, 10, empJeInternalCode);
//Save the employee labor grid data
if ($("#vendor").handsontable('getInstance') !== undefined) {
saveGridData("#hot");
}
}
// If the grid row changes, save the row and close any open dependent employee and vendor detail information
// If new grid row empLabor or vendLabor has value of "Y", open a new emp and/or vendor grid, loaded with
// information, if it exists.
function loadGridDetailForLine(obj) {
var lineNum = $(obj).handsontable('getDataAtCell', (this).row, 1);
}
</script>
<script type="text/javascript">
$(document).ready(function () {
//Discover partial rows and display error to user
//function alertPartialRows(gridData)
//{
// //gridData = $.parseJSON(gridData);
// $.each(gridData, function (i, item) {
// alert(item.toString());
// });
//}
////Remove empty rows. (Used before saving data.)
//function removeEmptyGridRows(obj)
//{
// //Get an instance of the object
// var currentGrid = $(obj).handsontable('getInstance');
// $.each (currentGrid.row){
// var isEmpty = isEmptyRow(currentGrid, currentGrid.row);
// alert("Row " + currentGrid.row + " is empty. Remove it.");
// }
//}
$("#save").click(function () {
//alert("Handler for save button has been called. Need conditional logic to save grid data only when data exists.");
saveGridData("#hot");
//If any detail grids are open, close them.
if ($("#emp").handsontable('getInstance') !== undefined) {
saveGridData("#emp");
closeGrid($("#emp"));
}
if ($("#vendor").handsontable('getInstance') !== undefined) {
saveGridData("#vendor");
closeGrid("#vendor");
}
});
$("#preValidateTxns").click(function () {
//alert("Handler for pre-validating transactions has been called.");
validateTransactions("#hot", false);
});
var hotRowActivePrevious = -1;
var hotRowActiveCurrent = -1;
var empRowActivePrevious = 0;
var empRowActiveCurrent = 0;
var vendorRowActivePrevious = 0;
var vendorRowActiveCurrent = 0;
//Initialize the filter/all data structures.
var allData;
var filterData;
// Create a new instance of the HandsOnTable by calling .handsontable method on a jQuery DOM object.
var $container = $("#hot");
$container.handsontable({
startRows: 1,
startCols: 1,
minRows: 0,
minCols: 0,
currentRowClassName: 'currentRow',
currentColClassName: 'currentCol',
rowHeaders: false,
colHeaders: function (col) {
switch (col) {
case 0:
return 'JET<br/>LN#';
case 1:
return "RT";
case 2:
return 'TRANS AMT';
case 3:
return 'GL ACCT';
case 4:
return 'ORG';
case 5:
return 'TRANS DESC';
case 6:
return 'PROJ #';
case 7:
return 'NOTES';
case 8:
return 'EMP<br/>LABOR';
case 9:
return 'VENDOR<br/>LABOR';
case 10:
return 'VALIDATION<br/>ERRORS';
case 11:
return 'PKJEUPLOAD';
case 12:
return 'JE INTERNAL<br/>CODE';
case 13:
return 'BILL<br/>FLAG';
case 14:
return 'REV<br/>FLAG';
case 15:
return 'TXN<br/>HEADER';
default:
return;
}
},
columns: [
{ data: 'lineNum', type: 'numeric', validator: lineValidator },
{ data: 'RT', type: 'text', default: "" },
{ data: 'txnAmount', type: 'numeric', format: '0,0.00', language: 'en' },
{ data: 'glAccount', type: 'text', default: "" },
{ data: 'org', type: 'text', default: "" }, //, validator: orgNumberValidator
{ data: 'txnDescr', type: 'text', default: "", validator: txnDescValidator },
{ data: 'projNumber', type: 'text', default: "" },
{ data: 'notes', type: 'text' },
{ data: 'empLabor', type: 'text', validator: empValidator },
{ data: 'vendorLabor', type: 'text', validator: vendValidator },
{ data: 'validationMessage', type: 'text' },
{ data: 'pkJeUpload', type: 'numeric', format: '0' },
{ data: 'jeInternalCode', type: 'text' },
{ data: 'bFlag', type: 'text', validator: bFlagUpdater },
{ data: 'rFlag', type: 'text', validator: rFlagUpdater },
{ data: 'hdrDescr', type: 'text', validator: txnHdrUpdater }
],
minSpareRows: 1,
contextMenu: true,
autoWrapRow: true,
autoWrapCol: true,
beforeRemoveRow: function (index, amount) {
//alert("We are in beforeRemoveRow");
for (var c = index; c < amount; c++) {
var arrayRowData = this.getDataAtRow(c);
var PkJeUpload = arrayRowData[11];
deleteRowData(PkJeUpload);
}
if ($("#emp").handsontable('getInstance') !== undefined) {
//alert("Closing the active emp grid.");
saveGridData("#emp");
closeGrid($("#emp"));
}
if ($("#vendor").handsontable('getInstance') !== undefined) {
//alert("Closing the active vendor grid.");
saveGridData("#vendor");
closeGrid("#vendor");
}
},
afterRemoveRow: function () {
//reload the grid with updated row numbers.
initData("#hot");
},
afterSelectionEnd: function (r, c, r2, c2) {
alert("Reached Hotgrid afterSelection");
if (r !== undefined) {
if (hotRowActiveCurrent != r) {
alert("The row has changed.");
//Save the master grid data
saveGridData("#hot");
//If there are open detail grids, save their data and close them
if ($("#emp").handsontable('getInstance') !== undefined) {
saveGridData("#emp");
closeGrid($("#emp"));
}
if ($("#vendor").handsontable('getInstance') !== undefined) {
saveGridData("#vendor");
closeGrid("#vendor");
}
//We have saved all of the current row data. Update the current row reference.
hotRowActiveCurrent = r;
//Get the data from the last populated row of the current grid to be carried into the new row
var totalRows = $("#hot").handsontable('countRows');
var rowIndex = 0;
//This is the first row
if (totalRows == 1) {
//A default transaction with the jeInternal code has been pre-loaded into the grid.
return;
}
else {
//If the new row is empty, create the new default row
if ($("#hot").handsontable('isEmptyRow', r))
//The grid is configured to always have one empty row at the end. In order to get the last row with data
//we need to offset the row count by 2.
{
var newRowData = {
newRT: $("#hot").handsontable('getDataAtRowProp', totalRows - 2, "RT")
, newLine: $("#hot").handsontable('getDataAtRowProp', totalRows - 2, "lineNum")
, newJeInternalCode: $("#hot").handsontable('getDataAtRowProp', totalRows - 2, "jeInternalCode")
, newBillFlag: $("#hot").handsontable('getDataAtRowProp', totalRows - 2, "bFlag")
, newRevFlag: $("#hot").handsontable('getDataAtRowProp', totalRows - 2, "rFlag")
, newTxnHeader: $("#hot").handsontable('getDataAtRowProp', totalRows - 2, "hdrDescr")
};
createDefaultGridRow("#hot", r, newRowData);
}
}
}
//Check for detail data in the new line.
checkForDetailData(r);
}
}
});
//Initialize the hot grid data. If JETransactions file exists, it loads existing data, otherwise loads default transaction line.
var outcome = initData("#hot");
$("#hot").handsontable("selectCell", 0, 0);
});
</script>
}
<div class="container-fluid">
<h2>@ViewBag.Title</h2>
<p>Use the grid below to enter transaction information. You can copy from an existing spreadsheet and paste into the grid in the web page. Use CNTL-C to copy and CNTL-V to paste (at the present time, right-click copy/paste functionality does not work).</p>
<p>@ViewBag.Message</p>
<div id="status" class="alert-success"></div>
<div id="hot"></div>
<div id="emp"></div>
<div id="vendor"></div>
<p>
<button name="save" id="save">Save</button>
<button name="preValidateTxns" id="preValidateTxns">Prevalidate Transactions</button>
</p>
</div>