We have coded our own solution to the problem. For anyone who is interested and our own reference, here it is. This isn't the ideal way to solve the problem, but it does work and was the path of least modified code. The ideal solution would be to create a new ajax call to refresh the footer in the table, instead we just piggy backed the refresh on the encoding of the table.
We used the concepts from this post:
viewtopic.php?f=3&t=6063&p=33836&hilit= ... ter#p33836 as a guide.
We modified 2 files to achieve this:
org.primefaces.component.datatable.DataTableRenderer
datatable.js
DataTableRenderer Mods:
We found that when a 'regular table' was being created the footer was being created before the table, this was an issue for us because when using a LazyDataModel, the data only loads when the encodeTbody() method is called. To remedy this we modified the encodeRegularTable method and rendered the footer after the body:
Code: Select all
protected void encodeRegularTable(FacesContext context, DataTable table) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("table", null);
encodeThead(context, table);
//Mods by Deltamation
//encodeTFoot(context, table);
encodeTbody(context, table);
encodeTFoot(context, table); //Moved to execute after so that the values
//can be used from the datatable's load() method
writer.endElement("table");
}
We found that the footer on the page was not given a generated ID, we needed this id to do the refresh via javascript, so we modified the encodeTFoot method to give the footer an id.
Code: Select all
protected void encodeTFoot(FacesContext context, DataTable table) throws IOException {
ResponseWriter writer = context.getResponseWriter();
ColumnGroup group = table.getColumnGroup("footer");
boolean shouldRender = table.hasFooterColumn() ||
(group != null && group.isRendered());
if(!shouldRender)
return;
writer.startElement("tfoot", null);
//Mods by Deltamation
String clientId = table.getClientId(context);
writer.writeAttribute("id", clientId + "_foot_data", null);
if(group != null) {
for(UIComponent child : group.getChildren()) {
if(child.isRendered() && child instanceof Row) {
Row footerRow = (Row) child;
writer.startElement("tr", null);
for(UIComponent footerRowChild : footerRow.getChildren()) {
if(footerRowChild.isRendered()
&& footerRowChild instanceof Column) {
encodeColumnFooter(context, table, (Column) footerRowChild);
}
}
writer.endElement("tr");
}
}
}
else {
writer.startElement("tr", null);
for(Column column : table.getColumns()) {
encodeColumnFooter(context, table, column);
}
writer.endElement("tr");
}
writer.endElement("tfoot");
}
In a refresh request the table contents are put into the response in the encodeEnd method, we added the encodeTFoot call to this so that whenever the table is refreshed the footer information goes along with it.
Code: Select all
@Override
public void encodeEnd(FacesContext context,
UIComponent component) throws IOException{
DataTable table = (DataTable) component;
if(table.isBodyUpdate(context)) {
encodeTbody(context, table);
//MOD BY DELTAMATION
encodeTFoot(context, table);
}
else if(table.isRowExpansionRequest(context)) {
encodeRowExpansion(context, table);
}
else if(table.isRowEditRequest(context)) {
encodeEditedRow(context, table);
}
else if(table.isScrollingRequest(context)) {
encodeLiveRows(context, table);
}
else {
encodeMarkup(context, table);
encodeScript(context, table);
}
}
Mods to datatable.js
The mods to the javascript were done to extract the footer that we placed into the response with the datatable.
First we needed the id for the footer, so we created a member in the class to hold it:
Code: Select all
/**
* PrimeFaces DataTable Widget
*/
PrimeFaces.widget.DataTable = function(id, cfg) {
this.id = id;
this.cfg = cfg;
this.jqId = PrimeFaces.escapeClientId(id);
this.jq = $(this.jqId);
this.tbody = this.jqId + '_data';
/** Mods by delta automation*/
this.tfoot = this.jqId + '_foot_data'
//Paginator
if(this.cfg.paginator) {
this.setupPaginator();
}
The other modification was to parse out the footer and replace it on the page whenever the table is being modified. This is done in a few methods:
PrimeFaces.widget.DataTable.prototype.paginate
Code: Select all
/**
* Ajax pagination
*/
PrimeFaces.widget.DataTable.prototype.paginate = function(newState) {
var options = {
source: this.id,
update: this.id,
process: this.id,
formId: this.cfg.formId
};
var _self = this;
options.onsuccess = function(responseXML) {
var xmlDoc = $(responseXML.documentElement),
updates = xmlDoc.find("update");
for(var i=0; i < updates.length; i++) {
var update = updates.eq(i),
id = update.attr('id'),
content = update.text();
/**** Mods by Deltamation ****/
if(id == _self.id){
//Find the id of the footer
var contentStr = content.toString();
var footIndex = contentStr.indexOf('<tfoot');
//Remove the footer information from the body
if(footIndex > 0){
content = contentStr.substring(0,footIndex);
}
$(_self.tbody).replaceWith(content);
if(footIndex > 0){
var footdata = contentStr.substring(footIndex);
$(_self.tfoot).replaceWith(footdata);
}
/** END Mods **/
_self.getPaginator().setState(newState);
if(_self.cfg.resizableColumns) {
_self.restoreColumnWidths();
}
}
else {
PrimeFaces.ajax.AjaxUtils.updateElement.call(this, id, content);
}
}
PrimeFaces.ajax.AjaxUtils.handleResponse.call(this, xmlDoc);
return true;
};
var params = {};
params[this.id + "_paging"] = true;
params[this.id + "_first"] = newState.recordOffset;
params[this.id + "_rows"] = newState.rowsPerPage;
params[this.id + "_page"] = newState.page;
params[this.id + "_updateBody"] = true;
options.params = params;
if(this.hasBehavior('page')) {
var pageBehavior = this.cfg.behaviors['page'];
pageBehavior.call(this, newState, options);
} else {
PrimeFaces.ajax.AjaxRequest(options);
}
}
and in PrimeFaces.widget.DataTable.prototype.sort
Code: Select all
/**
* Ajax sort
*/
PrimeFaces.widget.DataTable.prototype.sort = function(columnId, asc) {
if(this.isSelectionEnabled()) {
this.clearSelection();
}
var options = {
source: this.id,
update: this.id,
process: this.id,
formId: this.cfg.formId
};
var _self = this;
options.onsuccess = function(responseXML) {
var xmlDoc = $(responseXML.documentElement),
updates = xmlDoc.find("update");
for(var i=0; i < updates.length; i++) {
var update = updates.eq(i),
id = update.attr('id'),
content = update.text();
/**** MODS by Deltamation*******/
if(id == _self.id){
//Find the id of the footer
var contentStr = content.toString();
var footIndex = contentStr.indexOf('<tfoot');
//Remove the footer information from the body
if(footIndex > 0){
content = contentStr.substring(0,footIndex);
}
$(_self.tbody).replaceWith(content);
if(footIndex > 0){
var footdata = contentStr.substring(footIndex);
$(_self.tfoot).replaceWith(footdata);
}
/**** END MODS ****/
//reset paginator
var paginator = _self.getPaginator();
if(paginator) {
paginator.setPage(1, true);
}
if(_self.cfg.resizableColumns) {
_self.restoreColumnWidths();
}
}
else {
PrimeFaces.ajax.AjaxUtils.updateElement.call(this, id, content);
}
}
PrimeFaces.ajax.AjaxUtils.handleResponse.call(this, xmlDoc);
return true;
};
var params = {};
params[this.id + "_sorting"] = true;
params[this.id + "_sortKey"] = columnId;
params[this.id + "_sortDir"] = asc;
params[this.id + "_updateBody"] = true;
options.params = params;
if(this.hasBehavior('sort')) {
var sortBehavior = this.cfg.behaviors['sort'];
sortBehavior.call(this, columnId, options);
} else {
PrimeFaces.ajax.AjaxRequest(options);
}
}
and again in PrimeFaces.widget.DataTable.prototype.filter
Code: Select all
/**
* Ajax filter
*/
PrimeFaces.widget.DataTable.prototype.filter = function() {
if(this.isSelectionEnabled()) {
this.clearSelection();
}
var options = {
source: this.id,
update: this.id,
process: this.id,
formId: this.cfg.formId
};
var _self = this;
options.onsuccess = function(responseXML) {
var xmlDoc = $(responseXML.documentElement),
updates = xmlDoc.find("update");
for(var i=0; i < updates.length; i++) {
var update = updates.eq(i),
id = update.attr('id'),
content = update.text();
/**** MODS BY DELTAMATION*******/
if(id == _self.id){
//Find the id of the footer
var contentStr = content.toString();
var footIndex = contentStr.indexOf('<tfoot');
//Remove the footer information from the body
if(footIndex > 0){
content = contentStr.substring(0,footIndex);
}
$(_self.tbody).replaceWith(content);
if(footIndex > 0){
var footdata = contentStr.substring(footIndex);
$(_self.tfoot).replaceWith(footdata);
}
}
else {
PrimeFaces.ajax.AjaxUtils.updateElement.call(this, id, content);
}
}
PrimeFaces.ajax.AjaxUtils.handleResponse.call(this, xmlDoc);
//update paginator
var paginator = _self.getPaginator();
if(paginator) {
paginator.setPage(1, true);
paginator.setTotalRecords(this.args.totalRecords, true);
}
if(_self.cfg.resizableColumns) {
_self.restoreColumnWidths();
}
return true;
};
var params = {};
params[this.id + "_filtering"] = true;
params[this.id + "_updateBody"] = true;
options.params = params;
if(this.hasBehavior('filter')) {
var filterBehavior = this.cfg.behaviors['filter'];
filterBehavior.call(this, {}, options);
} else {
PrimeFaces.ajax.AjaxRequest(options);
}
}