Header and body column order mismatched when using exportOptions columns for print button

Header and body column order mismatched when using exportOptions columns for print button

stokes_dkstokes_dk Posts: 9Questions: 0Answers: 0

Link to test case: https://codepen.io/stokes_dk/pen/yyBYXYV
Debugger code (debug.datatables.net): opovot
Error messages shown:
Description of problem:
Use column visibility to hide the Position column then press the print button. In the output, the order of the header is Name, Position, ... Salary; the order of the body rows is Name, Office, ... Position.

Name Position Office Age Start date Salary
Ashton Cox San Francisco 66 2009-01-12 $86,000 Junior Technical Author
Cedric Kelly Edinburgh 22 2012-03-29 $433,060.00 Senior Javascript Developer

This happens when exportOptions for the print button are specified as:

exportOptions: {
    columns : [ ':visible', '.always-print' ]
}

and there is a hidden table column with the class 'always-print', e.g.:

<thead>
        <tr>
            <th>Name</th>
            <th class="always-print">Position</th>
            <th>Office</th>
            <th>Age</th>
            <th>Start date</th>
            <th>Salary</th>
        </tr>
</thead>

What I expected is that it would print only the visible columns but always include the Position column regardless of its visibility; in other words, :visible OR .always-print. What happens is that Position is always included but its placement in the header does not match its placement in the body rows.

Everything works as expected when the Position column is visible. When the Position column is hidden, this problem occurs.

I couldn't find anything in the docs for column-selector that says explicitly what should happen when ':visible' is used with a selector like '.always-print'. Some clarification in the docs would be helpful but presumably it is not intended to operate one way for the header and a different way for the body rows.

The same problem occurs with the csv export button.

Replies

  • allanallan Posts: 64,136Questions: 1Answers: 10,582 Site admin

    I believe this commit from October should fix this. I haven't released that yet, but will be doing so soon.

    With the nightly version it works as expected.

    Allan

  • stokes_dkstokes_dk Posts: 9Questions: 0Answers: 0

    Has this fix made it into a release yet?

  • kthorngrenkthorngren Posts: 21,789Questions: 26Answers: 5,041

    It looks like the fix was incorporated into DT 2.2.0. From the Release Notes:

    When using a CSS selector for the column selector (columns()) the order is now guaranteed to be in column index order.

    DT 2.2.2 is available from the Download Builder.

    Kevin

  • stokes_dkstokes_dk Posts: 9Questions: 0Answers: 0

    I still see the same problem: https://codepen.io/stokes_dk/pen/yyBYXYV

  • kthorngrenkthorngren Posts: 21,789Questions: 26Answers: 5,041

    The test case you just posted with 2.2.2 seems to work. For example hiding Position and Office:

    Results in this Print export:

    Are you expecting something different?

    Or can you provide the steps to show the issue you are seeing?

    Kevin

  • stokes_dkstokes_dk Posts: 9Questions: 0Answers: 0

    Yes. Look again at your screen capture. Under the Position heading is the age data.

  • kthorngrenkthorngren Posts: 21,789Questions: 26Answers: 5,041

    Look again at your screen capture. Under the Position heading is the age data.

    Yo have better vision than I :smile:

    @allan will need to take a look. Thanks for the additional info.

    Kevin

  • allanallan Posts: 64,136Questions: 1Answers: 10,582 Site admin

    Hi,

    It looks like my previous fix doesn't handle the case of using multiple selectors in an array unfortunately. However, there is a simple workaround. Combine your selector into one:

    columns : [ ':visible,.always-print' ]
    

    https://codepen.io/DataTables/pen/emYNYrY

    I'll look into address the array selector issue - thanks for brining it to my attention.

    Allan

  • allanallan Posts: 64,136Questions: 1Answers: 10,582 Site admin

    I've been mulling this over a bit and I'm a little weary of making a change for this. Consider the case where you specify columns [0, 8, 1], you would probably expect them to come back in the order specified. That is currently the case. A single selector will sort them, but an array will return in the array order.

    My current thinking is that should be defined as correct and documented as such.

    I'd welcome feedback on that thought process though!

    If I were to change it, it could be a breaking change and would be retained, probably until DataTables 3 (who knows when that will be!).

    Allan

  • stokes_dkstokes_dk Posts: 9Questions: 0Answers: 0

    Just to be clear, I'm not concerned about what order the columns are output, I'm saying that the order has to be consistent in the output header and body. If the output header columns are ordered A, B, C and the output body columns are A, C, B then that's a bug.

    In the screen capture above from Kevin the second column says Position in the header but contains cells from the Age column in the body.

    It looks to me like the header columns occur in the original sequence of the table but the body columns occur in the order specified in the array so the output is scrambled. I'm not sure why the code would use two different algorithms to determine the column sequence for the header and body but that appears to be what's happening.

    I can avoid this bug if I use a single string rather than an array of selectors, so that helps and I think I can do that in most of my cases. But for other people who need to use an array there's still a bug, right?

    -Dave.

  • allanallan Posts: 64,136Questions: 1Answers: 10,582 Site admin

    Hi Dave,

    I agree that there is an inconsistency between the header and the body - that is something I most certainly need to look at!

    Allan

  • allanallan Posts: 64,136Questions: 1Answers: 10,582 Site admin

    Following up on this to say that I've committed a fix to address the issue. I've also defined in the documentation what the behaviour should be and added tests to make sure it stays that way.

    Thanks,
    Allan

  • stokes_dkstokes_dk Posts: 9Questions: 0Answers: 0
    edited February 19

    Hi Allan, Thanks for the update on this. Documenting the behaviour would be helpful. Are you updating column-selector or another page? I think it's most important to be clear about which columns are included in the output; the order of the columns is less important but it's good to mention what the behaviour is rather than leave it unstated. It should be clear that the result is a union of the columns specified by the array.

    You had asked for thoughts on the column order of the output. I think it's probably best to stick with the order of columns as defined by the original table. Sometimes columns really should be next to each other like "First Name" and "Last Name", "Street Address" and "City", or "Start Date" and "End Date". I understand what you're saying, that if the user specifies columns [ 0, 8, 1 ] for export then they might expect the output to occur in that order but I don't think you can extend that logic to the general case. The array may contain a mix of things like [ 0, 8, 1, ':visible, .always-print' ]. I don't think it's the user's intent that the columns should be re-ordered so that visible columns occur after indexed columns; I think the intent is just to get the right set of columns. If the user really needs two columns to be adjacent but has to specify a mixed array for export then getting the right order becomes increasingly difficult (maybe impossible?). So I think it's best to stick with the original column order of the table.

    -Dave.

  • allanallan Posts: 64,136Questions: 1Answers: 10,582 Site admin

    Hi Dave,

    Yes, the column-selector is where I've added clarification on that. This is the specific bit.

    Many thanks for your thoughts! I've been continuing to ponder this, and I think if you explicitly ask for columns in a particular order, then they should be given back in that order. I accept that it might not always be what is needed - for such cases you could use columns().indexes() and then sort the indexes and reuse them as a new selector. More complex selectors or adding another class (e.g. the always-print one from your example) could also be an option.

    Not perfect, but I think always doing column order might be a limitation for some uses of the API.

    Allan

  • stokes_dkstokes_dk Posts: 9Questions: 0Answers: 0

    Hi Allan, I'm not clear what you mean when you say, "if you explicitly ask for columns in a particular order". In which cases is the user explicitly asking for an order and in which are they not? Does [ 0, 8, 1, ':visible' ] qualify? Or is the order explicit only in cases where indexes are specified in the array and nothing else, like [ 0, 8, 1 ]?

    As I understand it, a column-selector always represents a sequence of columns (not just a set). The order is always the same as the column order in the table (I think you call this "column index order") except when an array is specified. When using an array, the column order is determined by the order the columns are resolved in the array, for example [ 0, 8, 1, ':visible' ] when columns 5 and 8 are visible would result in a column sequence of 0, 8, 1, 5.

    Am I kind of getting it?

  • allanallan Posts: 64,136Questions: 1Answers: 10,582 Site admin

    [ 0, 8, 1, ':visible' ] would resolve to be [0, 8, 1, ...order-of-visible-columns...].

    If you wanted them in column index order, you'd use: :visible, :eq(0), :eq(8), :eq(1) - i.e. a single CSS selector.

    The way I've implemented it is that the CSS selector will resolve to column index order. If it is in an array, then the array order you specify is what is given back.

    Allan

  • stokes_dkstokes_dk Posts: 9Questions: 0Answers: 0

    Hi Allan,

    First of all, I wanted to say thank you for DataTables! It is amazing. I've been using it on various projects for many years and I can't tell you the countless hours it has saved me. It's one of my go-to components. I love it.

    I appreciate that you want to make the API intuitive and you don't want to have limitations. If I understand correctly, the concern is that there is no explicit way for the programmer to re-order columns so you're using an array column-selector as an implied ordering. But I think that also has its limitations: if I want don't want to re-order columns, I cannot use an array.

    If I need to select columns based on a variety of conditions like [ '.always-print', 8, function() ] and I want them in their current order, which I think is the normal case, what do I do? I need to figure out how to rewrite the selector so it's not an array.

    Could you extend selector-modifier to include a column-order for cases where re-ordering is needed? With rows, selecting and ordering are specified separately by row-selector and selector-modifier. Wouldn't it make sense to do the same for columns? column-order values could include 'current', 'index', and 'implied' (use the order implied by the array components).

    The default row ordering is 'current' (document order) which complies with the selectors API. Shouldn't the same be true for columns?

    -Dave.

  • allanallan Posts: 64,136Questions: 1Answers: 10,582 Site admin

    If I need to select columns based on a variety of conditions like [ '.always-print', 8, function() ] and I want them in their current order, which I think is the normal case, what do I do?

    If it wasn't a button, you'd use columns().indexes() and then .sort(). However, yes I can see that this is a problem for cases when there isn't that flexibility - e.g. the buttons export.

    Could you extend selector-modifier to include a column-order for cases where re-ordering is needed?

    I think that is a jolly good idea. I've got that noted down to look at doing for the next release. I'll try and implement it in the next few days.

    Allan

  • stokes_dkstokes_dk Posts: 9Questions: 0Answers: 0

    OK, great!

    If it wasn't a button, you'd use columns().indexes() and then .sort().

    Wouldn't this would give me index order? I don't know the API well enough but I suppose there is some way to get current document order, is that right? Maybe columns() returns them in document order, I don't see it specified in the docs.

    But if the user re-ordered columns and then pressed the print button I think they would expect to see their re-ordering applied to the printed version, even if the programmer restricts the columns to some subset. I think the current document order should always be the default unless, for whatever reason, the programmer feels it is absolutely necessary to explicitly override the column order.

    -Dave.

  • allanallan Posts: 64,136Questions: 1Answers: 10,582 Site admin

    Index order is document order for the columns in DataTables. That isn't the case with rows, but it is for columns.

    The ColReorder extension will actually modify the column order in DataTables' internals.

    Allan

Sign In or Register to comment.