Documentation error for mDataProp

Documentation error for mDataProp

timtuckertimtucker Posts: 48Questions: 0Answers: 0
edited February 2012 in DataTables 1.9
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

Replies

  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    Hi,

    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
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    I'm in the process of trying to figure out using a function with mDataProp and I seem to be finding more little things here and there... will separate things out into separate replies to make it easier to read:

    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]
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    In _fnGetCellData, the error message really needs to take into account if mDataProp is a function -- as-is, the message contains the entire body of the function, since adding in oCol.mDataProp just casts the function to a string:
    [code]
    _fnLog( oSettings, 0, "Requested unknown parameter '"+oCol.mDataProp+"' from the data source for row "+iRow );
    [/code]
  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    1. mDataProp as a function and fnRender - I would very much suggest against using both fnRender and mDataProp as a function. There is potential for conflict between the two (overwriting the stored property etc) and it doesn't seen a very efficient approach when mDataProp is already capable of doing everything needed. My suggestion would be to drop the use of fnRender if you are using mDataProp as a function (indeed, I would advocate in general not to use fnRender, but rather mDataProp as a function - the downside is that mDataProp can be a bit more complicated).

    > 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
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    Still trying to figure out when I should expect the mDataProp function to be called with "set" -- in my use I haven't seen it happen yet.

    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.
  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    > Still trying to figure out when I should expect the mDataProp function to be called with "set" -- in my use I haven't seen it happen yet.

    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
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    I went ahead and reduced it down to about as simple of a test case as I could come up with:
    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.
  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    Yup that's a bug! Thanks for the test case. I'll look at fixing it shortly :-)

    > 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
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    Also one of those odd things that doesn't affect anything -- is there any particular reason why about half of the comparisons for node names use toUpperCase() and half use toLowerCase()?
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    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...

    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
  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    > is there any particular reason why about half of the comparisons for node names use toUpperCase() and half use toLowerCase()?

    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
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    Here's the shortest I was able to get _fnApplyToChildren using the firstChild/nextChild approach (still ~100 bytes more than the original, though):
    [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]
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    edited February 2012
    Feeling bad about proposing something that added a little length to the code, I figured I might as well look for at least somewhere else that could be shortened up a bit, which led me to a chunk of code in _fnSortingClasses where it looked like it could be a little faster chaining together the jquery calls to remove/add classes:

    [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
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    edited February 2012
    And another interesting finding I came across: http://jsperf.com/for-v-pop/4

    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
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    edited February 2012
    Another another little bit from _fnSortingClasses:

    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
  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    Wowzer - some nice work there - thanks! I'm going to have to come back to these when I've got a little bit more time - discussion bookmarked so I can do so soon.

    Thanks!
    Allan
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    Any updates on the issue with mDataProp where it was never called for "set" (just type / display / filter / sort)?

    Was looking again at the JSbin test and it looks like the issue is still there in the latest nightly.
  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    The latest nightly should certainly be calling for set now. The example shown in the documentation for mDataProp should work as expected: http://datatables.net/docs/DataTables/1.9.0/DataTable.defaults.columns.html#mDataProp .

    If that doesn't help, can you link me to an example showing the issue please?

    Thanks,
    Allan
  • timtuckertimtucker Posts: 48Questions: 0Answers: 0
    It's the same as I'd listed earlier:
    http://live.datatables.net/ivunur

    And also:
    http://live.datatables.net/inidax/2
  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    This is actually a bit of a quirk in the current code - it isn't calling set on the mDataProp because it is never being set - its just using the value that you give it from the data source. If you use fnUpdate ( http://live.datatables.net/inidax/3/edit ) then 'set' is used.

    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
  • aura_petricaura_petric Posts: 4Questions: 0Answers: 0
    edited March 2012
    Hi 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
  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    DataTables 1.9 introduces the option to use aDataSort to sort a single column on multiple columns of data - however it is limited to sort both columns in the same direction - that limitation is a bit of a bummer here, since otherwise it would be trivial to use that option. I'm going to be looking at improving the sorting options in future to cope with that.

    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
  • aura_petricaura_petric Posts: 4Questions: 0Answers: 0
    Thank you for your reply. Here is what I am trying to do:

    "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.
  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    I'd say yes - this is absolutely along the lines I was thinking of for the second option above. I think you can probably have setSortingColumns() return just an object rather than an array with an object in it though.

    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
  • allanallan Posts: 63,692Questions: 1Answers: 10,500 Site admin
    Old thread - however, regarding this point:

    > 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
  • aura_petricaura_petric Posts: 4Questions: 0Answers: 0
    Hi 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.
This discussion has been closed.