mData as a function doesn't appear to work with JS array as data source

mData as a function doesn't appear to work with JS array as data source

ascendantascendant Posts: 7Questions: 0Answers: 0
edited August 2012 in Bug reports
I recently started working with mData as a function, but was stymied when I got a DataTables warning that said “Requested unknown parameter {mData function} from the data source for row 0”

To try and diagnose what was wrong, I started with the example from the “Orthogonal Data” blog post which introduced the idea (http://datatables.net/media/blog/orthogonal_data/currency.html) and added in my code until I could make it break.

What I discovered was that using mData as a function appears to give the error message when data is provided as a JavaScript array (which is what I was originally using) but not when the data is provided in the HTML table itself (as in the “Orthogonal Data” example.)

I’ve illustrated this with some examples at http://luminousvictus.com/datatablestest/DataTables-1.9.3/

On that page, there are four links. The first, TABLEdata-WITHmdata, is essentially the same as the “Orthogonal Data” example, and renders identically.

The second link, TABLEdata-NOmdata, is the same as the previous link except that the “mData” statement is commented out. It still renders fine, although of course the currency formatting isn’t there anymore, which mData was handling.

The third link, JSdata-NOmdata, is the same as the second, except that the data source is a Javascript array (with the exact same data. The way it renders is different from TABLEdata-NOmdata in that the columns have different widths, but still it looks fine and everything works the same.

The fourth link, JSdata-WITHmdata, is the same as JSdata-NOmdata, except that the original “mData” statement from the first link has now been uncommented. This triggers the DataTables warning, and all cells in the “salary” column render as “$null”.

I haven’t actually delved into the guts of DataTables itself, so this is pure speculation- but I’m wondering if perhaps when mData is used as a function, it attempts to get “val” from the table’s DOM, but due to being instantiated from a JS array, the HTML table cells’ values haven’t been set yet when when the mData function is called, and thus it’s passing a null value.

Replies

  • allanallan Posts: 63,381Questions: 1Answers: 10,449 Site admin
    Hi,

    Thanks for posting up the test cases!

    I don't think that this is actually a bug, but more of quirk of how mData works (mRender is the solution to the quirk) :-). Let me explain:

    When using mData as as a string or an integer (default) then it will read the property from the data source related to the value you give it - however, in the case of passing in a function, DataTables doesn't have that reference to the data you want to use - thus it can't pass anything in as the 'val' parameter to the function (the quirk is that it actually does for HTML sourced data! This in turn is because we know the index automatically and can pass there data in - perhaps that is wrong...).

    So in fact to have your array mData example work you would do this:

    [code]
    {
    "aTargets": [ 1 ],
    "mData":function( data, type, val ) {
    if (type === "set") {
    data.price = data[1];
    data.price_display = data[1]==="" ? "" : "$"+data[1];
    data.price_filter = data[1]==="" ? "" : data.price_display+" "+data[1];
    return;
    }
    else if (type === "display") {
    return data.price_display;
    }
    else if (type === "filter") {
    return data.price_filter;
    }
    return data[1];
    }
    }
    [/code]

    Note how `val` is not used, but rather `data[1]` is!

    So moving on to a better solution - mRender and mData together:

    DataTables 1.9.3 introduced a new option call mRender which is the compliment to mData. mData tells DataTables what data to use, while mRender tells it how to display that data (it is basically mData, but without a 'set' option. So here you would do:

    [code]
    {
    "aTargets": [ 1 ],
    "mData": 1,
    "mRender":function( data, type, full ) {
    return (type === "display" || type === "filter") ?
    "$"+data : data;
    }
    }
    [/code]

    Much easier :-). Two things to note here:

    1. mData in this case is actually optional since DataTables actually sets mData to the column index value if mData is not given - i.e. it would be set to 1 automatically.

    2. I've combined display and filter into a single return, which I think is all that is needed in this case, since you aren't formatting the data.

    Hope this explains it adequately!

    Regards,
    Allan
  • ascendantascendant Posts: 7Questions: 0Answers: 0
    Hi Allan,

    Thank you for your detailed response, it makes more sense now :) I was thinking of using mRender, but I saw that it was new and that there would be a blog post coming out soon, so I opted for the more documented mData (though clearly, I wasn't aware of the implications at the time)

    It wasn't originally clear to me from the documentation that mData functions should act on their arguments differently depending on the way the data source is set.

    Initially, I was operating under the assumption, that the way data is sent to DataTables is independent of how it is later acted on. Kind of like how, when you have a choice to write either
    [code]myArray = ['foo', 'bar'];[/code]
    or
    [code]myArray = new Array('foo', 'bar');[/code]
    choosing between those two doesn't affect the way you will later interact with myArray- I'd made the assumption that this was the case in choosing the data source method for DataTables vis a vis later interacting with that data as well. It might be helpful to new users to indicate where this isn't the case on the data sourcing examples, or mData etc. documentation.

    Cheers,
    Chris
  • allanallan Posts: 63,381Questions: 1Answers: 10,449 Site admin
    Hi Chris,

    You are absolutely right - there will be a blog post about mRender - I've not just had a chance to sit down a write it yet :-(.

    mData as a function does have a place for certain - it is easier to implement caching for example if you for a complex formatting method that you don't want to run every time a 'get' is used. There needs to be a balance between the use of mRender and mData, and likely every case will be different!

    I also agree with the statement about how the data is acted upon depending on the source. That shortcut I've got in for DOM sourced data possibly wasn't a good idea. It might be painful to remove now though unfortunately, so the only way out probably is to improve the documentation for it.

    Thanks for the feedback!
    Allan
This discussion has been closed.