Documentation error for mDataProp
Documentation error for mDataProp
timtucker
Posts: 48Questions: 0Answers: 0
Noticed that the docs for mDataProp say:
[quote]null - the sDafaultContent option will use used for the cell (empty string by default. This can be useful on generated columns such as edit / delete action columns.[/quote]
Two issues there:
- "sDefaultContent" is misspelled
- "sDefaultContent" looks like it defaults to null rather than an empty string
[quote]null - the sDafaultContent option will use used for the cell (empty string by default. This can be useful on generated columns such as edit / delete action columns.[/quote]
Two issues there:
- "sDefaultContent" is misspelled
- "sDefaultContent" looks like it defaults to null rather than an empty string
This discussion has been closed.
Replies
Thanks very much for flagging these two errors up! I've just fixed them (you are absolutely correct, sDefaultContent is null by default) and improved the sentence a bit (I really did manage to butcher those few lines... :-( ).
The documentation is tied to the releases of DataTables now, so the fixes won't show up in the online documentation until the next release (I suspect probably about a week away - letting the dust settle from the 1.9.0 release :-) ).
Thanks again!
Regards,
Allan
If I'm seeing things correctly, _fnGatherData is setting the innerHTML for cells twice if fnRender is defined
[code]
if ( typeof oCol.mDataProp === 'function' )
{
nCell.innerHTML = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
}
/* Rendering */
if ( bRender )
{
sRendered = _fnRender( oSettings, iRow, iColumn );
nCell.innerHTML = sRendered;
if ( oCol.bUseRendered )
{
/* Use the rendered data for filtering/sorting */
_fnSetCellData( oSettings, iRow, iColumn, sRendered );
}
}
[/code]
It would seem like it would be more efficient to do:
[code]
/* Rendering */
if ( bRender )
{
sRendered = _fnRender( oSettings, iRow, iColumn );
nCell.innerHTML = sRendered;
if ( oCol.bUseRendered )
{
/* Use the rendered data for filtering/sorting */
_fnSetCellData( oSettings, iRow, iColumn, sRendered );
}
}
else if ( typeof oCol.mDataProp === 'function' )
{
nCell.innerHTML = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
}
[/code]
[code]
_fnLog( oSettings, 0, "Requested unknown parameter '"+oCol.mDataProp+"' from the data source for row "+iRow );
[/code]
> In _fnGetCellData, the error message really needs to take into account if mDataProp is a function
Good point! I'll add in a check for that.
Allan
Should I expect it to get called when I'm adding an array of objects via fnAddData?
If I have mDataProp defined for a column, I see 1 call for "type", then calls for "display" and "filter" -- no calls for "set".
I could make the first call to "display" act as a "set" to cache the generated display content for the cell, but that seemed like the intent of "set", so I wanted to make sure that I'm not missing something.
Firstly it will only happen in 1.9.0+, so if you are using anything before that, its upgrade time :-). It will be called with 'set' whenever DataTables reads data from whatever source - DOM or Javascript arrays / Ajax.
> Should I expect it to get called when I'm adding an array of objects via fnAddData?
Yes, absolutely!
Can you post a link to the page that isn't working as expected?
Allan
http://live.datatables.net/ivunur
Watching the output in the console, I would expect to see a call to "set" -- instead all I see is type, display, filter, sort.
(Filter seems slightly unexpected to me since there's no filter set. I would think that filter would only need to be called when changing the filter or if there's already a filter set before adding data.
> Filter seems slightly unexpected to me since there's no filter set.
It is called as DataTables builds a cache of the filtering strings to try and speed things up overall, rather than building the filter array when filtering it required which would result in a significant overhead when filtering.
Allan
Looking at _fnApplyToChildren, instead of:
[code]
for (var i = 0, iLen = an1.length; i < iLen; i++)
{
for (var j = 0, jLen = an1[i].childNodes.length; j < jLen; j++)
{
if (an1[i].childNodes[j].nodeType == 1)
{
if (an2)
{
fn(an1[i].childNodes[j], an2[i].childNodes[j]);
}
else
{
fn(an1[i].childNodes[j]);
}
}
}
}
[/code]
It probably could be faster as:
[code]
for ( var i=0, iLen = an1.length; i < iLen ; i++ )
{
var nParent = an1[i];
var aChildren = nParent.childNodes;
for ( var j=0, jLen=aChildren.length ; j < jLen ; j++ )
{
var nChild = aChildren[j];
if ( nChild.nodeType == 1 )
{
if ( an2 )
{
fn( nChild, an2[i].childNodes[j] );
}
else
{
fn( nChild );
}
}
}
}
[/code]
Or potentially a little more optimized for the case where an2 is passed in:
[code]
if (an2)
{
for ( var i=0, iLen = an1.length; i < iLen ; i++ )
{
var nParent = an1[i], nParent2 = an2[i];
var aChildren = nParent.childNodes, aChildren2 = nParent2.childNodes;
for ( var j=0, jLen=aChildren.length ; j < jLen ; j++ )
{
var nChild = aChildren[j];
if ( nChild.nodeType == 1 )
{
fn( nChild, aChildren2[j] );
}
}
}
}
else
{
for ( var i=0, iLen = an1.length; i < iLen ; i++ )
{
var nParent = an1[i];
var aChildren = nParent.childNodes;
for ( var j=0, jLen=aChildren.length ; j < jLen ; j++ )
{
var nChild = aChildren[j];
if ( nChild.nodeType == 1 )
{
fn( nChild );
}
}
}
}
[/code]
jsperf test for the idea (at least at a higher level): http://jsperf.com/nextsibling-vs-childnodes/4
No - no reason at all bother than be being a bit inconsistent :-(. As long as the test matches the transform used then it is fine :-)
> Also another little thing that I noticed -- remembering what I'd found before about lookups for childNodes in fnRender, I figured that the same technique could probably be applied elsewhere...
Yup good plan! next sibling would be a sensible move there. Thanks for highlighting that!
Allan
[code]
for ( var i=0, iLen = an1.length; i < iLen ; i++ )
{
var nChild = an1[i].firstChild;
if (an2)
{
var nChild2 = an2[i].firstChild;
while (nChild)
{
if ( nChild.nodeType == 1 )
{
fn( nChild, nChild2 );
}
nChild = nChild.nextSibling;
nChild2 = nChild2.nextSibling;
}
}
else
{
while (nChild)
{
if ( nChild.nodeType == 1 )
{
fn( nChild );
}
nChild = nChild.nextSibling;
}
}
}
[/code]
[code]
if ( oSettings.aaSortingFixed !== null )
{
aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
}
else
{
aaSort = oSettings.aaSorting.slice();
}
/* Apply the required classes to the header */
var oCol, jqTh;
for ( i=0 ; i
Probably quite a few for loops on temporary arrays where a switch to using shift/pop might cut down on code size and speed things up.
So in _fnGatherData, since it doesn't matter if we destroy what's in nTrs (since it's not used later in the function),
[code]
nTrs = _fnGetTrNodes( oSettings );
nTds = [];
for ( i=0, iLen=nTrs.length ; i
Under the assumption that any use of sClass is a prefix reserved by DataTables, this:
[code]
if ( nTds[i].className.indexOf(sClass+"1") != -1 )
{
for ( j=0, jLen=(nTds.length/iColumns) ; j
Thanks!
Allan
Was looking again at the JSbin test and it looks like the issue is still there in the latest nightly.
If that doesn't help, can you link me to an example showing the issue please?
Thanks,
Allan
http://live.datatables.net/ivunur
And also:
http://live.datatables.net/inidax/2
I can absolutely see that this is a bit counter intuitive - I think that this will need to be looked at a bit closer. The thing that I'm concerned about is that introduce the set function for every single field is that it is going to slow things down a fair bit.
Allan
I have this structure of json data:
{
"aaData": [
{
"DT_RowId": "ed595cd5-f5bf-4e54-92fa-44132cbcfa55",
"Position": {
"Pos": "1",
"Status": 1,
"Rank": "1"
},
"Total": "260"
},
{
"DT_RowId": "10cb627b-f721-461b-ba61-fc2b8c821fc6",
"Position": {
"Pos": "T2",
"Status": 1,
"Rank": "2"
},
"Total": "268"
},
{
"DT_RowId": "c71970dc-bf15-4a27-8110-d17271d8e983",
"Position": {
"Pos": "T3",
"Status": 2,
"Rank": "3"
},
"Total": "276"
}
]
}
and what I am currently struggling with is the sorting on "Position". I tried using the "mDataProp" function that comes with DT 1.9.0 and managed to use "Pos" for display and "Status" for sorting but I need a more complex functionality. Basically I need a way to differentiate between 'asc' and 'desc' sorting and do the following: when 'asc' sorting, sort both on "Status" and "Rank" asc and when 'desc' sorting I need to sort first by "Status" asc and then by "Rank" desc.
Is this achievable using the "mDataProp" function or the only way to do this is by capturing the click on the "Position" column? Does DT 1.9.0 facilitate the column headers click somehow?
My column definition islike this:
aoColumnDefs: [
{
"aTargets": [0],
"mDataProp": "DT_RowId"
},
{
"aTargets": [1],
"mDataProp": function(source, type, val){
if(type === 'set'){
source.Position.Status = val.Position.Status;
source.Position.Rank = val.Position.Rank;
source.Position.Pos = "" + val.Position.Pos + "";
return;
}
else if(type === 'display' || type === 'filter'){
return "" + source.Position.Pos + "";
}
//'sort'
return source.Position.Status;
}
},
{
"aTargets": [2],
"mDataProp": "Total"
}
]
Many thanks.
Aura
Until then, I think there are two options:
1. Unbind the click listener that DataTables adds by default to the header cells and add your own which will call fnSort with the multi-dimentional sorting that you want (possibly the easiest option).
2. Use mDataProp to return an object that contains the two data points for each row that will then be passed to the sorting functions and create a sorting plug-in which will sort the data the way you want (possibly the more flexible option).
Allan
"mDataProp": function (source, type, val) {
if (type === 'display' || type === 'filter') {
return "" + source.Position.Pos + "";
}
// 'sort' and 'type' both just use the integer
return setSortingColumns(source);
},
"sType": "position"
}
.....
jQuery.fn.dataTableExt.oSort['position-asc'] = function (a, b) {
var sortingColumnsA = setSortingColumns(a);
var sortingColumnsB = setSortingColumns(b);
return ((sortingColumnsA.status < sortingColumnsB.status) ? -1 : ((sortingColumnsA.status > sortingColumnsB.status) ? 1 : 0));
};
jQuery.fn.dataTableExt.oSort['position-desc'] = function (a, b) {
var sortingColumnsA = setSortingColumns(a);
var sortingColumnsB = setSortingColumns(b);
return ((sortingColumnsA.status < sortingColumnsB.status) ? -1 : ((sortingColumnsA.status > sortingColumnsB.status) ? 1 : 0));
};
function setSortingColumns(source) {
var sortingColumns = [];
sortingColumns.push({ status: source.Position.Status, rank: source.Position.Rank });
return sortingColumns;
}
Ignoring the errors that I might have introduced, do you think I am on the right way on doing this? Is the "mDataProp" definition I made compatible with the custom sorting type "position"? As I am debugging the code and cannot make it pass through the plug-in methods.
Thanks.
Regarding the 'position' type - try passing your table through the debugger, just to check that the type is being picked up correctly ( http://debug.datatables.net ) - it should be from that.
Allan
> In _fnGetCellData, the error message really needs to take into account if mDataProp is a function
This has now been fixed in 1.9.1.dev and will be in the 1.9.1 release.
Allan
I managed to sort my table using mDataProp and some sorting plugins together. I am now facing another challenge. When sorting by certain columns I need to also filter the data and show only the rows that fit a specific criteria.
I've seen some examples on how to create custom filtering plugins but I wonder if there is a way to call that filtering function from inside mDataProp definition, something like this:
{ "aTargets": [11],
"mDataProp": function (source, type) {
if (type === 'display' || type === 'filter') {
if (source.ID != '') {
if (source.Status < 2) {
return "" + source.Total + "";
}
return "";
}
return "";
}
var col = setSortingColumns(source);
var oTable = lb_settings.table;
oTable.fnDraw(); //here should be the call to the filtering plugin
return col;
}
}
Or is there a way to filter on the source properties like for example: oTable.fnFilter(source.ID != '') ?
Thanks again for you tips.