iSeller Commerce
iSeller POS Retail
iSeller POS F&B
iSeller POS Express
Crosslight
WebUI
ClientUI
What's New
Download Trial
Web Solution
Mobile Solution
Enterprise Solution
Custom Development
Blog
Community
Latest Development Blogs
ForumPostTopic
Browse By Tag
I have a WebGrid with a footer with each column being summed via the AggregateFunction w/ "SUM". This work as expected. However, after changing the cells on the client-side, this data is not updated.
I did some research and noticed the WebGrid's OnExitEditMode client-side event to get the new value but I can't seem to figure out how to determine to get that updated field's column for each row in the grid as well as the underlying footer cell to update.
Is there a way to do this without using JavaScript on the client-side? If so, can you provide an example on how do to this?
Here's my implementation that I hope will help someone else. This would be a nice feature to the product for anyone using the BatchUpdate feature. The purpose of this script is to automatically update the grid's footer cell as each editable cell is modified
function grdTreatyClaims_OnAfterExitEditMode(controlID, tableName, editObject) { try { var grdTreatyClaims = ISGetObject(controlID); if (grdTreatyClaims == null) { return; } if (editObject == null) { return; } // Get updated column's name/value var colName = editObject.ToCellObject().Name; var colNewValue = editObject.element.value; // Get footer column's new value var colFooterNewValue = WebGrid_GetColumnAggregateValue(controlID, colName, null); // Update footer column WebGrid_SetFooterColumnValue(controlID, colName, colFooterNewValue); return true; } catch (ex) { ShowJSException(ex); } }
function WebGrid_GetColumnAggregateValue(gridID, colName, aggregateFunction) { try { // Get grid var grid = ISGetObject(gridID); if (grid == null) { return null; } // Get grid's rows var rows = grid.RootTable.GetElement(WG40.BODY, WG40.HTMLTABLE).getElementsByTagName("tr"); if (rows == null) { return null; } // Get aggregate function based on specified column if not specified // NOTE: Default "Sum" if column is not found if (IsNullOrEmpty(aggregateFunction) == true) { var column = WebGrid_GetColumnByName(gridID, colName); if (column != null) { aggregateFunction = column.AggregateFunction; } else { aggregateFunction = "Sum"; } } // Execute aggregate function // NOTE: Check for "Record" type var value = 0; for (var counter = 0; counter < rows.length; counter++) { var row = rows[counter]; if (row != null) { if (row.type == "Record") { var rowCol = grid.GetRowByElement(row).GetCell(colName); if (rowCol != null) { // Perform aggregate switch (aggregateFunction) { case "Sum": value += Number(rowCol.Value); break; } } } } } return value; } catch (ex) { ShowJSException(ex); } } function WebGrid_GetColumnByName(gridID, colName) { try { // Get grid's column by name var grid = ISGetObject(gridID); if (grid == null) { return null; } return grid.RootTable.Columns.GetNamedItem(colName); } catch (ex) { ShowJSException(ex); } } function WebGrid_GetHeaderCells(gridID) { try { // Get header cells var grid = ISGetObject(gridID); if (grid == null) { return null; } return grid.RootTable.GetElement(WG40.COLHEADER, WG40.HTMLDIV).getElementsByTagName("td"); } catch (ex) { ShowJSException(ex); } } function WebGrid_GetFooterCells(gridID) { try { var grid = ISGetObject(gridID); if (grid == null) { return null; } return grid.RootTable.GetElement(WG40.COLFOOTER, WG40.HTMLDIV).getElementsByTagName("tr")[1].getElementsByTagName("td"); } catch (ex) { ShowJSException(ex); } } function WebGrid_GetFooterColumnByName(gridID, colName) { // Get grid's column index for update var colIndex = WebGrid_GetColumnIndexByName(gridID, colName); if (colIndex < 0) { return null; } // Get the grid's footer cells var footerCells = WebGrid_GetFooterCells(gridID); if (footerCells == null) { return null; } // Get footer column return footerCells[colIndex]; } function WebGrid_GetColumnIndexByName(gridID, colName) { try { // Grid ID is required if (IsNullOrEmpty(gridID) == true) { return null; } // Column Name is required if (IsNullOrEmpty(colName) == true) { return null; } // Get grid's header cells var headerCells = WebGrid_GetHeaderCells(gridID); if (headerCells == null) { return null; } // Get grid's column index by name var index = -1; for (var counter = 0; counter < headerCells.length; counter++) { var headerCell = headerCells[counter]; if (headerCell != null) { var headerCellName = headerCell.getAttribute("ColName"); if (headerCellName == colName) { index = counter; break; } } } return index; } catch (ex) { ShowJSException(ex); } } function WebGrid_SetFooterColumnValue(gridID, colName, value) { try { // Get grid's column index for update var colIndex = WebGrid_GetColumnIndexByName(gridID, colName); if (colIndex < 0) { return false; } // Get the grid's footer cells var footerCells = WebGrid_GetFooterCells(gridID); if (footerCells == null) { return false; } // Set footer column's text with specified value var footerCell = footerCells[colIndex]; if (footerCell != null) { footerCell.innerHTML = value; } return true; } catch (ex) { ShowJSException(ex); } }
Hi Shawn,
When you changed cells on the client side, are they also getting updated? If you only set the text on the cells without update, of course footer would not get updated. But you can try to update it manually by aggregate them manually and set the footer by getting the element first. FYI, the element of footer depends on the structure you have.
Regards,Handy
Hi Shawn,When you changed cells on the client side, are they also getting updated? If you only set the text on the cells without update, of course footer would not get updated. But you can try to update it manually by aggregate them manually and set the footer by getting the element first. FYI, the element of footer depends on the structure you have.Regards,Handy
Yes, there is no server-side postback. Everything is done for client-side.
Is there a way to iterate through the grid's row for the particular column that has been updated? The OnExitEditMode event can be used but not sure if the editObject knows which column has been edited. Does it know via name or index? I am also not sure how to iterate through the footer's columns. I noticed an example using index. I can use that assuming the editObject knows its index.
Thinking about this more.
Does it make more sense to handle the OnInitializePostBack event that occurs after the cell value is updated and the row is changed? It's possible to update the underlying DataSource and let the grid re-calculate the footer totals. Actually, at that point, it seems like the changed cell's data is gone...
Also, is there a way to turn that off? Strictly just keep it 100% client-side?
No, I suggest the same with you. All the changes should be handled in client side. If you only want to see if the cell is changed or not, you can get old value and new value in onExitEditMode event. Please try to create a simple sample that replicates this issue and I will try to modify it for you.
I can't provide a sample as I can't seem to find anything in the client-side object model that will help me. I can use the onExitEditMode event to re-calculate the footer columns. But, I have a few questions:
1) Is there a way to indicate either the index or name of the field being changed within the onExitEditMode? If so, I can use that name/index to determine the column being updated and then iterate through the grid's columns and sum the fields accordingly.
2) Using the sum value in #1, how do I retrieve the footer columns? I would need to retrieve this column similiar to the approach in question #1 so I can update the appropriate column.
Thanks.
In ExitEditMode, you could use the edit object parameter to determine the column that is being modified. Here is the snippet:
function WebGrid1_OnExitEditMode(ctrlId, tblName, editObj){ var grid = ISGetObject(ctrlId); var colName = editObj.ToCellObject().Name;}
In order to retrieve the sum value in the footer row, you will need to access the footer HTML element using method describe in Switching to new Client Side API article in the WebGrid documentation. Here is the code snippet, showing how to retrieve the footer of field "Quantity":
function getFooterValue(){ var grid = ISGetObject("WebGrid1"); var tgtColName = "Quantity"; var tgt = 0; var headerCells = grid.RootTable.GetElement(WG40.COLHEADER, WG40.HTMLDIV).getElementsByTagName("td"); for(var i = 0; i < headerCells.length; i++) { var headerCell = headerCells[i]; var colName = headerCell.getAttribute("ColName"); if(colName == tgtColName) tgt = i; } var footerCells = grid.RootTable.GetElement(WG40.COLFOOTER, WG40.HTMLDIV).getElementsByTagName("tr")[1].getElementsByTagName("td"); var footerCellLabel = footerCells[tgt].innerHTML; var footerCellText = grid.RootTable.Columns.GetNamedItem('Quantity').FooterText; alert(footerCellLabel.replace(footerCellText, "")); }
While in BatchUpdate mode, footer would not be updated until you accept all the changes. Your approach can be used as references for the others who also used BatchUpdate mode. We are really appreciate for your post.
While in BatchUpdate mode, footer would not be updated until you accept all the changes. Your approach can be used as references for the others who also used BatchUpdate mode. We are really appreciate for your post.Regards,Handy
No problem. I am glad to help given the help I have received.
This is working good except filtering does not work. Is there any way for filtering to work with the data entered on the client-side? It won't filter against any of the updated cells.
If I am not mistaken, that could not be done. Even though the data are inserted in the grid, they have not been saved in database. So, it will filter only the records in database. Note: Sorting also the same
This approach seems to work quite well for your average grid, and the techinique is used throughout my team's application.
However, I'm unable to adapt it for use with a *child grid*, and the error I'm getting implies that there's something strange going on inside the GetElement method.
This code works just fine:
?grid.RootTable.GetElement(WG40.BODY, WG40.HTMLTABLE).getElementsByTagName("tr") {Count = 40} [Raw View]: {object} [0]: {object} // etc
But when I try to reference a child table, I get a very strange error:
?grid.RootTable.ChildTables[0].GetElement(WG40.BODY, WG40.HTMLTABLE).getElementsByTagName("tr") 'tagName' is null or not an object
The error seems to be coming from the GetElement method, because it's the same when I don't call the .getElementsByTagName method:
?grid.RootTable.ChildTables[0].GetElement(WG40.BODY, WG40.HTMLTABLE) 'tagName' is null or not an object
Both grid.RootTable and grid.RootTable.ChildTables[0] return a valid-looking WebGridTable object, so I would figure I'm on the right track. Is there a different way I should be accessing the Child table, perhaps?
Hello,
You could not get the child table just using that code. You need to get SubTable first. Then, the element tag name should be available.
(Sorry for the delayed reply... I didn't realize I needed to turn on reply notification)
The suggestion for using SubTable helped a lot! I can iterate through the rows. But I can't seem to find the cell where I need to update the total.
In the snippet below, I'm able to iterate through the rows by substituting the SubTable object for the RootTable object:
var grid = ISGetObject(gridID); // Get parent or child WebGridRow object //var theTable = grid.RootTable; // parent var theTable = grid.GetSelectedObject().ToRowObject().SubTable; // child var rows = theTable.GetElement(WG40.BODY, WG40.HTMLTABLE).getElementsByTagName("tr"); // iterate through all rows for (var i = 0; i < rows.length; i++) { if (rows[i].type == "Record") { sCellValue = grid.GetRowByElement(rows[i]).GetCell(columName).Value;
With a similar substitution, I'm able to correctly grab the index to the header column. The GetHeaderCells function returns an array of 20 columns, and my "Percent" column is [7]:
headerCells = theTable.GetElement(WG40.COLHEADER, WG40.HTMLDIV).getElementsByTagName("td");
But something's wrong with the code that's supposed to get all the footer cells. There are only 2 elements returned by this call, and neither is my "Percent" column:
footerCells = theTable.GetElement(WG40.COLFOOTER, WG40.HTMLDIV).getElementsByTagName("tr")[1].getElementsByTagName("td");
I suspect I need to be looking somewhere other than row index [1]... but where?
Please send me your runable sample. If it is possible, send along with your current structure. The elements are changed depend on your WebGrid Structure. If you want me to check it for you, it would be better if you provide me your runable sample.
I'm attaching samples that I hope will help. I think I've removed everything that won't compile outside our environment.
I put alert boxes at two spots in the code. One shows that we did successfully scroll through the child rows and compute a total... the other shows that the function that was supposed to return the footer row, didn't do so.
Thanks a ton for your assistance! It's *so* close now!
Note: Y'all might want to test the "Attach File" function with Chrome (20.0.1132.34 beta-m). I had some trouble. :)
Hello Robert,
Where are your .js files? I noticed that it calls your .js files. I would need to have those scripts in order to get your current logic.
Sorry about that. You can remove the .js references and add this at the beginning of the <script> section:
function OnAfterInitialize(a, b, c, d) { var grid = ISGetObject(a); if (grid.RootTable.GetRowsCount() == 0) grid.ResetStatus(true); }
Thanks!
You could not just get the div then use getElementsByTagName like that. It is not recommended because the WebGrid itself use many tables collection in there include for the footers. I would recommend you to find the correct child element manually.
e.g
alert(theTable.GetElement(WG40.COLFOOTER, WG40.HTMLCELL).children[1].children[1].children[6].innerText);
This is the footer element I found in your structure. Please let me know if it works well on you or not.
That gets me the footer element, if I know it's element 6.
How do I implement the more generic solution, where we use GetColumnIndexByName/GetHeaderCells to get the element number? It returns 8, which sends me to the wrong child.
I tried getting the header info using something similar, but the direct equivalent (using WG40.COLHEADER instead of WG40.COLFOOTER) doesn't work. In fact, there isn't even a children[1].
?theTable.GetElement(WG40.COLHEADER, WG40.HTMLCELL).children {Count = 1} [Raw View]: {object} [0]: {object}
There's also the question: if we shouldn't be using "getElementsByTagName", then why is that the only method mentioned in this thread until now? Should we be using "children" (and WG40.HTMLCELL instead of WG40.HTMLTABLE) in the original solution as well? If so, we need new equivalents for these:
// Get grid's rows var rows = grid.RootTable.GetElement(WG40.BODY, WG40.HTMLTABLE).getElementsByTagName("tr"); // Get header cells return grid.RootTable.GetElement(WG40.COLHEADER, WG40.HTMLDIV).getElementsByTagName("td"); // Get footer cells return grid.RootTable.GetElement(WG40.COLFOOTER, WG40.HTMLDIV).getElementsByTagName("tr")[1].getElementsByTagName("td");
Ok, I did some poking and found what appears to be the solution. Making these changes allows the calling functions to remain unchanged.
Old (getElementsByTagName):
function GetHeaderCells(gridID) { var grid = ISGetObject(gridID); if (grid == null) { return null; } // Get parent or child WebGridRow object //var theTable = grid.RootTable; var theTable = grid.GetSelectedObject().ToRowObject().SubTable; return theTable.GetElement(WG40.COLHEADER, WG40.HTMLDIV).getElementsByTagName("td"); } function GetFooterCells(gridID) { var grid = ISGetObject(gridID); if (grid == null) { return null; } // Get parent or child WebGridRow object //var theTable = grid.RootTable; var theTable = grid.GetSelectedObject().ToRowObject().SubTable; return theTable.GetElement(WG40.COLFOOTER, WG40.HTMLDIV).getElementsByTagName("tr")[1].getElementsByTagName("td"); }
New (children):
function GetHeaderCells(gridID) { var grid = ISGetObject(gridID); if (grid == null) { return null; } // Get parent or child WebGridRow object //var theTable = grid.RootTable; var theTable = grid.GetSelectedObject().ToRowObject().SubTable; return theTable.GetElement(WG40.COLHEADER, WG40.HTMLCELL).children[0].children[0].children; } function GetFooterCells(gridID) { var grid = ISGetObject(gridID); if (grid == null) { return null; } // Get parent or child WebGridRow object //var theTable = grid.RootTable; var theTable = grid.GetSelectedObject().ToRowObject().SubTable; return theTable.GetElement(WG40.COLFOOTER, WG40.HTMLCELL).children[1].children[1].children; }
Of course, I'm worried about the possiblity that I got the right answer from the wrong place, so if you could confirm that what I'm going to use is stable, I'd appreciate it.
Since we are going to play with the element, there is no the exact or proper method/location such as properties. It depends on the structure. When the structure is changed the elements are also changed. You need to adjust it by using debugger and watch the current value/element.
or
Choose this if you're already a member of Intersoft Community Forum. You can link your OpenID account to your existing Intersoft Social ID.
Choose this if you don't have an Intersoft account yet. Your authenticated OpenID will be automatically linked to your new Intersoft account.
Enter your Wordpress Blogname