fnDraw in revised fnDataUpdate
fnDraw in revised fnDataUpdate
dmclean
Posts: 55Questions: 2Answers: 0
I was using fnDataUpdate (API plug-in) and the table was not showing any updates so I modified it (as shown below). The table still isn't showing any updates. Both the enter and end messages are coming out in the log. I am at a loss as to what to try next and would greatly appreciate some suggestions.
Thank you,
Donald
[code]
$.fn.dataTableExt.oApi.fnDataUpdate = function (oSettings, nRowObject, iRowIndex) {
try {
console.log("[fnDataUpdate] enter.");
var dataRow = oSettings.aoData[iRowIndex]._aData;
$(nRowObject).find("td").each(function(i) {
dataRow[i] = $(this).html();
});
// var iPos = oSettings._iDisplayStart;
this.oApi._fnDraw(oSettings, true);
console.log("[fnDataUpdate] end.");
}
catch(e) {
console.log("[fnDataUpdate] threw exception.")
}
};
[/code]
Thank you,
Donald
[code]
$.fn.dataTableExt.oApi.fnDataUpdate = function (oSettings, nRowObject, iRowIndex) {
try {
console.log("[fnDataUpdate] enter.");
var dataRow = oSettings.aoData[iRowIndex]._aData;
$(nRowObject).find("td").each(function(i) {
dataRow[i] = $(this).html();
});
// var iPos = oSettings._iDisplayStart;
this.oApi._fnDraw(oSettings, true);
console.log("[fnDataUpdate] end.");
}
catch(e) {
console.log("[fnDataUpdate] threw exception.")
}
};
[/code]
This discussion has been closed.
Replies
How are you using DataTables with regard to what the data source is? Are you using mDataProp for example - because if so this will not work (the function needs to be updated for that... - added to the to-do list). If you are just using a DOM data source or a plain array then it looks okay... One thing to note - you say "the table still isn't showing any updates" - it wouldn't do when you run this function - it is designed for updating the internal cache of DataTables based on the DOM (i.e. you might have changed the DOM for this row). Generally speaking you would want to use fnUpdate ( http://datatables.net/api#fnUpdate ).
Regards,
Allan
After looking at fnUpdate more carefully, I tried the code shown below. That didn't work - I kept getting an error:
Uncaught TypeError: Object # has no method 'fnFindCellRowIndexes'
There should be a way to make this work - another suggestion would be lovely.
Thank you,
Donald
[code]
$.fn.dataTableExt.oApi.fnDataUpdate = function (oSettings, nRowObject, iRowIndex) {
try {
console.log("[fnDataUpdate] enter.");
var dataRow = oSettings.aoData[iRowIndex]._aData;
var data[] = $(nRowObject).find("td");
this.oApi._fnUpdate(oSettings, data, iRowIndex)
console.log("[fnDataUpdate] end.");
}
catch(e) {
console.log("[fnDataUpdate] threw exception.")
}
};
[/code]
I've no idea what fnFindCellRowIndexes is I'm afraid it doesn't appear to be in DataTables or any of the 'extras'.
Allan
Ok, so what I tried next is:
1. Update the DOM directly (trivially easy).
2. Get the row index (from the plug-in API):
var oTableIndex = oTable.fnFindCellRowIndexes()[0];
3. Get the DOM for the TR (from the plug-in API):
var oTableRow = oTable.fnFindCellRowNodes()[0];
4. Call fnDataUpdate (modified from the plug-in API - see below):
oTable.fnDataUpdate(oTableRow, oTableIndex);
What happens is that the entire row is ending up in the first column of the table. DOM looks like this:
....
Something is obviously going wrong in either fnFindCellRowNodes or fnDataUpdate, but I'm not sure what.
Thank you,
Donald
[code]
$.fn.dataTableExt.oApi.fnDataUpdate = function (oSettings, nRowObject, iRowIndex) {
try {
console.log(nRowObject);
var dataRow = oSettings.aoData[iRowIndex]._aData;
$(nRowObject).find("td").each(function(i) {
dataRow[i] = $(this).html();
});
this.oApi._fnDraw(oSettings, true);
}
catch(e) {
console.log("[fnDataUpdate] threw exception: " + e);
}
};
[/code]
I have to say I'm not massively convinced by the fnDataUpdate function looking at it more closely - it seems that nRowObject is a bit redundant... You could do something like this:
[code]
$.fn.dataTableExt.oApi.fnDataUpdate = function (oSettings, iRowIndex) {
try {
console.log(nRowObject);
var nRow = Settings.aoData[iRowIndex].nTr;
var dataRow = oSettings.aoData[iRowIndex]._aData;
$('td', nRow).each(function(i) {
dataRow[i] = $(this).html();
});
this.oApi._fnDraw(oSettings, true);
}
catch(e) {
console.log("[fnDataUpdate] threw exception: " + e);
}
};
[/code]
With this you don't need the call to fnFindCellRowNodes (which sounds like it would be quite a slow operation with fnFindCellRowIndexes as well).
Given that you've updated the DOM you must have a reference to the TR node already? In which case you could use fnGetPosition ( http://datatables.net/api#fnGetPosition ) which will be faster and less complex.
Regards,
Allan
And as a result sorting doesn't work.
Allan
In the meantime, I'll have to continue trying things based on your suggestions. I've included the specific HTML from the page before and after an update. If you have another idea that I can try I'm all ears.
Thank you,
Donald
The original HTML for one row:
[code]
2011-03-14T08:49:57
11031412495570753gaffney
gaffney70753
NORMAL
2264:18
Staging
moved to trouble
1
35
0/32
[/code]
The HTML for the same row after it has been updated and fnDraw has been called:
[code]
2011-03-14T08:49:57
11031412495570753gaffney
gaffney70753
NORMAL
2264:32
Staging
moved to trouble
1
35
0/32
[/code]
Thanks,
Allan
One of the complicating factors here is that I'm using an advanced web framework (Lift - http://liftweb.net/) and some things are very easy (Comet updates). However, the documentation is not very mature and so it's very easy to confuse similar functionality - such as replacing an entire or replacing just the contents.
Thank you for your help.
[code]
try { destroy_F822621353229GKMKJ1(); } catch (e) {}
try {
var parent1 = document.getElementById("row.11061516535370819gaffney");
parent1.innerHTML = "2011-06-15T12:53:5311061516535370819gaffneygaffney70819NORMAL44:32Local complete-succeeded 21010/10";
for (var i = 0; i < parent1.childNodes.length; i++) {
var node = parent1.childNodes[i];
parent1.parentNode.insertBefore(node.cloneNode(true), parent1);
}
parent1.parentNode.removeChild(parent1);
} catch (e) {
// if the node doesn't exist or something else bad happens
}
var oTableIndex = oTable.fnFindCellRowIndexes("11061516535370819gaffney")[0];
oTable.fnDataUpdate(oTableIndex);
console.log("Done for: row.11061516535370819gaffney.");
try { destroy_F822621353229GKMKJ1 = function() {}; } catch (e) {}
if (lift_toWatch['F822621353229GKMKJ1'] !== undefined) lift_toWatch['F822621353229GKMKJ1'] = '822621353709';
[/code]
This means that I'm going to have to code around the problem because that means that I can't easily update properties of the TR as part of a single call. Blech.
Any idea how much work it would take to fix this problem?
[code]
try { destroy_F400000897468E4QTBO(); } catch (e) {}
try {
var parent1 = document.getElementById("row.11031413100570758gaffney");
parent1.innerHTML = "\u000a 2011-03-14T09:10:0611031413100570758gaffneygaffney70758NORMAL2282:02Staging KILLED 170/6\u000a ";
for (var i = 0; i < parent1.childNodes.length; i++) {
var node = parent1.childNodes[i];
parent1.parentNode.insertBefore(node.cloneNode(true), parent1);
}
parent1.parentNode.removeChild(parent1);
} catch (e) {
// if the node doesn't exist or something else bad happens
}
var oTableIndex = oTable.fnFindCellRowIndexes("11031413100570758gaffney")[0];
oTable.fnDataUpdate(oTableIndex);
console.log("Done for: row.11031413100570758gaffney.");
try { destroy_F400000897468E4QTBO = function() {}; } catch (e) {}
if (lift_toWatch['F400000897468E4QTBO'] !== undefined) lift_toWatch['F400000897468E4QTBO'] = '400000897534';
[/code]
So given that you have complete control over the insertion of the new data from the Comet library, why not just use fnUpdate ( http://datatables.net/api#fnUpdate ). DataTables will take care of updating the internal cache and the DOM for you, all you need to do is tell it what cell or row you want to update and feed it the data.
An alternative would be to use fnDelete to remove the old row and then fnAddData to insert the new row. Note that for the checkbox you would need to store the state of it and then restore it after the add (since again it's a new DOM element).
Basically DataTables can't know when you modify the table's DOM so it's best to try and use the API functions for updating information in the table - and I suspect that it might make things a bit simpler for you here.
Allan
1. At the moment, it's kind of ugly. The Lift framework makes it dead easy to compose a TR and throw it to the client. I'll probably end up writing a Lift widget if I get it working properly.
2. The update shown below doesn't work - the check boxes disappear. If I wrap them it a they're ok, but I shouldn't have to do that.
3. Scroll position is lost. My customers will never accept that. Is there a way to preserve scroll position?
[code]
oTable.fnUpdate([,'2011-03-14T08:49:57','11031412495570753gaffney','gaffney70753','NORMAL','2284:54','Staging','moved to trouble','1','35','0/32',], oTableIndex);
[/code]
2. and 3. I think the answer for these lie together. What you want is the 'no-redraw' option of fnUpdate (param 4: http://datatables.net/api#fnUpdate ). That will stop a full sort and filter occurring. DataTables will do a full sort and filter when information is updated by default - this will stop that and stop the full redraw (which must take the table back to row 0). If you do want to do the full redraw, then you could just save the scroll position and then restore it after the fnUpdate call.
So with the checkbox, as I mentioned you would need to store it's state, but there is a different way of approaching this - doing cell by cell updates:
[code]
var liftCells = $('td', liftRow);
oTable.fnUpdate( lifeCells[0].innerHTML, rowIndex, 0, false );
oTable.fnUpdate( lifeCells[1].innerHTML, rowIndex, 1, false );
oTable.fnUpdate( lifeCells[2].innerHTML, rowIndex, 2, false );
...
[/code]
and just don't update any cells which don't need to be updated with new information from the server (including the checkbox).
Does that sound workable?
Regards,
Allan
I do need to do the redraw since some updates could affect sort order, so I looked at the plug-in API to try and create a function that would save the position, do the update and then restore the position. What I tried didn't work, perhaps you can tell me what I did wrong?
[code]
$.fn.dataTableExt.oApi.fnRevisedUpdate = function (oSettings, data, rPos) {
if (typeof bRedraw == 'undefined') {
bRedraw = true;
}
var pos = oSettings._iDisplayStart;
this.oApi._fnUpdate(oSettings, data, rPos);
oSettings._iDisplayStart = pos;
oSettings.oApi._fnCalculateEnd(oSettings);
this.oApi._fnDraw(oSettings, true);
};
[/code]
Allan
[code]
"sScrollY": "600px",
"bPaginate": false,
[/code]
Allan
One note about updates with check boxes (and probably other HTML elements) - you may want to add this to the documentation for fnUpdate: the whole thing must be wrapped in tick marks (as shown in the example below). Firefox can and will parse it without the tick marks, but Chrome and Safari will not.
It may be that this seems obvious to most people, but it wasn't obvious to me and since I'm not quite a complete idiot (yet), perhaps a little clarification will save somebody some pain and aggravation.
And just so we're clear: Allan, you ROCK, dude!
[code]
oTable.fnUpdateWOScroll([
'',
'2011-03-22T07:10:19',
'11032211101870764kyp',
'kyp70764','NORMAL','2163:39',
'SFTP','moved to trouble',
'1',
'24',
'0/23',
'',
'movedtotrouble'],
oTableIndex);
[/code]
[code]
$.fn.dataTableExt.oApi.fnUpdateWOScroll = function (oSettings, data, rPos) {
var pos = $('div.dataTables_scrollBody').scrollTop();
this.fnUpdate(data, rPos);
$('div.dataTables_scrollBody').scrollTop(pos);
};
$.fn.dataTableExt.oApi.fnAddWOScroll = function (oSettings, data) {
var pos = $('div.dataTables_scrollBody').scrollTop();
this.fnAddData(data);
$('div.dataTables_scrollBody').scrollTop(pos);
};
[/code]
Though I did notice that calling delete with an undefined "rPos" leads to an ugly error that is not easy to trace back to the real cause. Thus the check that I added.
[code]
$.fn.dataTableExt.oApi.fnDeleteWOScroll = function (oSettings, rPos) {
if (typeof rPos == 'undefined') {
console.log("[fnDeleteWOScroll] invalid delete, do nothing.");
return;
}
try {
var pos = $('div.dataTables_scrollBody').scrollTop();
this.fnDeleteRow(rPos);
$('div.dataTables_scrollBody').scrollTop(pos);
}
catch(e) {
console.log("Exception: ");
console.log(e);
}
};
[/code]
> It may be that this seems obvious to most people, but it wasn't obvious to me and since I'm not quite a complete idiot (yet)
I think this is an entirely fair point. I'll look at improving the documentation there. Might be suitable for a future blog post as well...
Regards,
Allan
I know this is old news, but I have to say, DataTables is amazing!
Also, I wanted to use fnDataUpdate. However I faced issues with the code. So I made a couple of tweaks and it worked just fine. Hope it is useful for some one. Feel free to clean up the code.
[code]
$.fn.dataTableExt.oApi.fnDataUpdate = function (oSettings, nTr, iRowIndex) {
var nTds = nTr.getElementsByTagName('td');
if ( nTds.length != oSettings.aoColumns.length )
{
alert( 'Warning: not adding new TR - columns and TD elements must match' );
return;
}
var dataRow = oSettings.aoData[iRowIndex]._aData;
$('td', nTr).each(function(i) {
dataRow[i] = $(this).html();
});
for ( iLen=oSettings.aoColumns.length, i=iLen-1 ; i>-1 ; i-- )
{
if ( !oSettings.aoColumns[i].bVisible)
{
nTr.removeChild(nTds[i]);
}
}
oSettings.aoData[iRowIndex].nTr=nTr;
oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
this.oApi._fnDraw(oSettings, true);
}
};
[/code]