How to selectively bring responsive detail columns to the foreground

How to selectively bring responsive detail columns to the foreground

flaymanflayman Posts: 3Questions: 1Answers: 0

Hello,

I'm looking for advice on the best approach for selectively un-hiding columns that are hidden by the responsive extension either because of the width of the table or because of class names. The opposite is also true. I'd like to selectively relegate columns to details. In looking through the documentation I have seen colReorder and the visibility button advertised as ways to control the responsive column visibility, but I've found these to be contrary to expectations. colReorder doesn't really have any effect on what columns are shown, and the visibility buttons do not bring columns out of the detail row and into the table. A column in the detail row is visible according to the button state. Clicking its button will actually remove that column from all the details, which is not what I would have expected. I'm considering working on a custom set of buttons that change responsive priorities, probably as an extension of the visibility button logic. Any advice would be appreciated. Thanks.

Regards,
Matt

This question has an accepted answers - jump to answer

Answers

  • rf1234rf1234 Posts: 2,949Questions: 87Answers: 416
    edited June 2020 Answer ✓

    you could assign classes to the columns dynamically according to the values of the respective buttons. Subsequently do a responsive.recalc() and columns.adjust(). You'll find these in the docs by searching.

    It might not work because some of the input values are set during dt initialization. Then you would need a page refresh. But you'll figure it out :smile:

  • flaymanflayman Posts: 3Questions: 1Answers: 0

    Thank you for this advice. I will see whether I can extend the colVis button and override its functionality in order to make it work better with responsive for my purposes.

  • flaymanflayman Posts: 3Questions: 1Answers: 0
    edited June 2020

    I have come up with a solution that works, but I'm not entirely happy with it. I was not able to simply override parts of the existing colVis button because I couldn't work out how to get at the functions brought in through the "extends" chain, so I had to resort to making my own extension which provides all the same layers. Here is the code of my extension, which I've named colvisResponsive (and colvisResponsiveRestore):

    /*!
     * Based on column visibility buttons for Buttons and DataTables.
     * buttons.colVis.js
     */
    
    (function( factory ){
        if ( typeof define === 'function' && define.amd ) {
            // AMD
            define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
                return factory( $, window, document );
            } );
        }
        else if ( typeof exports === 'object' ) {
            // CommonJS
            module.exports = function (root, $) {
                if ( ! root ) {
                    root = window;
                }
    
                if ( ! $ || ! $.fn.dataTable ) {
                    $ = require('datatables.net')(root, $).$;
                }
    
                if ( ! $.fn.dataTable.Buttons ) {
                    require('datatables.net-buttons')(root, $);
                }
    
                return factory( $, root, root.document );
            };
        }
        else {
            // Browser
            factory( jQuery, window, document );
        }
    }(function( $, window, document, undefined ) {
    'use strict';
    var DataTable = $.fn.dataTable;
    
    
    $.extend( DataTable.ext.buttons, {
        // A collection of column visibility buttons that behaves properly with responsive
        colvisResponsive: function ( dt, conf ) {
            return {
                extend: 'collection',
                text: function ( dt ) {
                    return dt.i18n( 'buttons.colvis', 'Column visibility' );
                },
                className: 'buttons-colvis',
                buttons: [ {
                    extend: 'columnsToggle',
                    columns: conf.columns,
                    columnText: conf.columnText
                } ]
            };
        },
    
        // Selected columns with individual buttons - toggle column visibility
        columnsToggle: function ( dt, conf ) {
            var columns = dt.columns( conf.columns ).indexes().map( function ( idx ) {
                return {
                    extend: 'columnToggle',
                    columns: idx,
                    columnText: conf.columnText
                };
            } ).toArray();
    
            return columns;
        },
    
        // Single button to toggle column visibility
        columnToggle: function ( dt, conf ) {
            return {
                extend: 'columnVisibility',
                columns: conf.columns,
                columnText: conf.columnText
            };
        },
    
        // Selected columns with individual buttons - set column visibility
        columnsVisibility: function ( dt, conf ) {
            var columns = dt.columns( conf.columns ).indexes().map( function ( idx ) {
                return {
                    extend: 'columnVisibility',
                    columns: idx,
                    visibility: conf.visibility,
                    columnText: conf.columnText
                };
            } ).toArray();
    
            return columns;
        },
    
        // Single button to set column visibility
        columnVisibility: {
            columns: undefined, // column selector
            text: function ( dt, button, conf ) {
                return conf._columnText( dt, conf );
            },
            className: 'buttons-columnVisibility',
            action: function ( e, dt, button, conf ) {
                var col = dt.columns( conf.columns );
                var curr = col.visible();
                var visible = col.responsiveHidden()[0];
    
                var header = col.header().to$();
                if (visible) {
                    header.removeClass('all');
                    header.addClass('none');
                } else {
                    header.removeClass('none');
                    header.addClass('all');
                }
                dt.responsive.recalc();
                dt.columns.adjust();
                // This is what redraws the table
                col.visible(curr);
                dt.button(button).active(!visible);
            },
            init: function ( dt, button, conf ) {
                var that = this;
                button.attr( 'data-cv-idx', conf.columns );
    
                dt
                    .on( 'column-visibility.dt'+conf.namespace, function (e, settings) {
                        if ( ! settings.bDestroying && settings.nTable == dt.settings()[0].nTable ) {
                            that.active( dt.column( conf.columns ).responsiveHidden() );
                        }
                    } )
                    .on( 'column-reorder.dt'+conf.namespace, function (e, settings, details) {
                        if ( dt.columns( conf.columns ).count() !== 1 ) {
                            return;
                        }
    
                        // This button controls the same column index but the text for the column has
                        // changed
                        that.text( conf._columnText( dt, conf ) );
    
                        // Since its a different column, we need to check its visibility
                        that.active( dt.column( conf.columns ).responsiveHidden() );
                    } )
                    .on( 'draw.dt'+conf.namespace, function( e, settings ) {
                        dt.responsive.recalc();
                        dt.columns.adjust();
                        that.active( dt.column( conf.columns ).responsiveHidden() );
                    } )
                    .on( 'responsive-resize.dt'+conf.namespace, function( e, dt, columns ) {
                        that.active( columns[conf.columns] );
                    } );
            },
            destroy: function ( dt, button, conf ) {
                dt
                    .off( 'column-visibility.dt'+conf.namespace )
                    .off( 'column-reorder.dt'+conf.namespace )
                    .off( 'draw.dt'+conf.namespace )
                    .off( 'responsive-resize.dt'+conf.namespace );
            },
    
            _columnText: function ( dt, conf ) {
                // Use DataTables' internal data structure until this is presented
                // is a public API. The other option is to use
                // `$( column(col).node() ).text()` but the node might not have been
                // populated when Buttons is constructed.
                var idx = dt.column( conf.columns ).index();
                var title = dt.settings()[0].aoColumns[ idx ].sTitle
                    .replace(/\n/g," ")        // remove new lines
                    .replace(/<br\s*\/?>/gi, " ")  // replace line breaks with spaces
                    .replace(/<select(.*?)<\/select>/g, "") // remove select tags, including options text
                    .replace(/<!\-\-.*?\-\->/g, "") // strip HTML comments
                    .replace(/<.*?>/g, "")   // strip HTML
                    .replace(/^\s+|\s+$/g,""); // trim
    
                return conf.columnText ?
                    conf.columnText( dt, idx, title ) :
                    title;
            }
        },
    
    
        colvisResponsiveRestore: {
            className: 'buttons-colvisRestore',
    
            text: function ( dt ) {
                return dt.i18n( 'buttons.colvisRestore', 'Restore visibility' );
            },
    
            init: function ( dt, button, conf ) {
                dt.on('draw.dt'+conf.namespace, function() {
                    conf._visOriginal = dt.columns().indexes().map( function ( idx ) {
                        var header = dt.column( idx ).header();
                        return header.className;
                    } ).toArray();
                } );
            },
    
            action: function ( e, dt, button, conf ) {
                dt.columns().every( function ( i ) {
                    // Take into account that ColReorder might have disrupted our
                    // indexes
                    var idx = dt.colReorder && dt.colReorder.transpose ?
                        dt.colReorder.transpose( i, 'toOriginal' ) :
                        i;
                                    var column = dt.column( idx );
                                    var visible = column.visible();
                    var className = conf._visOriginal[ idx ];
                    column.header().className = className;
                    this.visible( visible );
                } );
            }
        },
    
    
        colvisGroup: {
            className: 'buttons-colvisGroup',
    
            action: function ( e, dt, button, conf ) {
                dt.columns( conf.show ).visible( true, false );
                dt.columns( conf.hide ).visible( false, false );
    
                dt.columns.adjust();
            },
    
            show: [],
    
            hide: []
        }
    } );
    
    
    return DataTable.Buttons;
    }));
    

    Note, I did not change the spec of the covisGroup button because I'm not using groups and I haven't looked at the use cases. Everything else works very well though. The state of the buttons are correct and they react immediately to other columns automatically coming in and out due to singular additions or substractions. If someone can tell me how I could have done this without creating the entire new extension, I'd be grateful.

    As an aside, I was surprised that the built in colVis button did not work this way when combined with responsive. In my opinion, the button should be aware of responsive if it is loaded. Thanks.

This discussion has been closed.