Child rows + filter widget + Multiple Headers #113

Open
smu2015 opened this Issue Jul 26, 2012 · 15 comments

Projects

None yet

3 participants

@smu2015
smu2015 commented Jul 26, 2012

Hello,

i would like to create a table with :

  • 2 rows for the headers (for childrow)
  • Filter for each cells

The filter work only for the first row of the thead.

Do you know an issue for my problem plz ?
Very sry for my english :) I'm french

Collaborator

http://mottie.github.com/tablesorter/docs/#Configuration

check out the 'filter_childRows' option

smu2015 commented Jul 26, 2012

Thank you for your answer :)

I use this parameter (filter_childRows : true) but i search for a search's input who is looking only in child rows like this :

http://hpics.li/4ce1b37

Owner
Mottie commented Jul 26, 2012

HI smu2015!

What filter program are you using? That doesn't look like the filter widget provided with the plugin. Did you see this demo?

smu2015 commented Jul 26, 2012

Hi :)

I'm using your jquery.tablesorter.widgets.js and i try to create a type "Filter_multiple".
It's homemade but my beginner's level in jQuery don't permiss me more :)

smu2015 commented Jul 26, 2012

And yes, i have see the demo.
But i would like search only on :

  • childRow without primary
  • Primary without Childrow

Congratulation for your tablesorter, it's very cool and easy. It's a pleasure to use it

Owner
Mottie commented Jul 26, 2012

If you have any code you would like to share, I would like to see it. But right now, I am very busy with updating to version 2.4 and starting work on version 3.0, so I may not have time to modify the current filter widget for you; but if you already have something written, I don't mind helping.

Maybe there is another filter plugin somewhere that can be modified?

smu2015 commented Jul 27, 2012

I have just copy your widget filter and try to adapt for my table.

The modif is principaly on : http://hpics.li/7295819

I know, it's not a good modification but i try :)

// code removed to avoid confusion, but thank you anyway :)
Owner
Mottie commented Jul 27, 2012

Ok, I didn't spend too much time with this, but I didn't notice any errors and I think it's working as it should. I'm not sure if I plan on officially supporting this mod, but if you have any problems, please feel free to post here.

This filter widget mod will not:

  • Add a select dropdown
  • Use filter functions ( no filter_functions option )
  • Include child row text ( no filter_childRows option )
  • Allow using parsed data for child rows

This demo has a rowspan in the first cell, but I don't think it matters; and I don't have your HTML to test it.

Here is a demo

To set this filter widget up, you need to add an extra tbody with all of your filters. Each filter has a data-column="#" in which the # matches the column number (zero-based index, but ignore rowspan & colspan cells).

<tbody class="tablesorter-infoOnly">
    <tr class="tablesorter-filter-row">
        <td colspan="5">
            Order #: <input type="search" placeholder="" data-column="0">
            Customer: <input type="search" placeholder="" data-column="1">
            PO: <input type="search" placeholder="" data-column="2">
            Date: <input type="search" placeholder="" data-column="3">
            Total: <input type="search" placeholder="" data-column="4">
            Address: <input type="search" placeholder="" data-column="5">
            City/State: <input type="search" placeholder="" data-column="6">
            Zip: <input type="search" placeholder="" data-column="7">
            Notes: <input type="search" placeholder="" data-column="8">
        </td>
    </tr>
</tbody>

and here is the filter widget mod code

/* Widget: modified filter (7/27/2012)
 widgetOptions:
  filter_cssFilter     : 'tablesorter-filter' // css class name added to the filter row & each input in the row
  filter_ignoreCase    : true   // if true, make all searches case-insensitive
  filter_reset         : null   // jQuery selector string of an element used to reset the filters
  filter_searchDelay   : 300    // typing delay in milliseconds before starting a search
  filter_startsWith    : false  // if true, filter start from the beginning of the cell contents
  filter_useParsedData : false  // filter all data using parsed content
 **************************/
$.tablesorter.addWidget({
    id: "filter",
    format: function(table) {
        if (table.config.parsers && !$(table).hasClass('hasFilters')) {
            var i, j, k, l, cv, v, val, ff, x, xi, cr, st, sel, str,
            ft, ft2, $tb, $th, $tr, $td, rg, r, s, t, dis, col,
            last = '', // save last filter search
            ts = $.tablesorter,
            c = table.config,
            $ths = $(c.headerList),
            wo = c.widgetOptions,
            css = wo.filter_cssFilter || 'tablesorter-filter',
            $t = $(table).addClass('hasFilters'),
            b = $t.children('tbody:not(.' + c.cssInfoBlock + ')'),
            cols = c.parsers.length,
            reg = [ // regex used in filter "check" functions
                /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // 0 = regex to test for regex
                new RegExp(c.cssChildRow), // 1 = child row
                /undefined|number/, // 2 = check type
                /(^[\"|\'|=])|([\"|\'|=]$)/, // 3 = exact match
                /[\"\'=]/g, // 4 = replace exact match flags
                /[^\w,. \-()]/g, // 5 = replace non-digits (from digit & currency parser)
                /[<>=]/g // 6 = replace operators
            ],
            parsed = $ths.map(function(i){
                return (ts.getData) ? ts.getData($ths.filter('[data-column="' + i + '"]:last'), c.headers[i], 'filter') === 'parsed' : $(this).hasClass('filter-parsed');
            }).get(),
            time, timer,

            // dig fer gold
            findRows = function(filter){
                var $tb, $tr, $td, cr, r, v, cv, l, ff, time, arry;
                if (c.debug) { time = new Date(); }
                arry = $.isArray(filter);
                v = (arry) ? filter : $t.find('tbody').eq(0).children('tr').find('input.' + css).map(function(){
                    return $(this).val() || '';
                }).get();
                cv = (v || []).join(''); // combined filter values
                // return if the last search is the same; but filter === false when updating the search
                // see example-widget-filter.html filter toggle buttons
                if (last === cv && filter !== false) { return; }

                $t.trigger('filterStart');
                for (k = 0; k < b.length; k++ ) {
                    $tb = $(b[k]).hide();
                    $tr = $tb.children('tr');
                    l = $tr.length;
                    if (cv === '') {
                        $tr.show().removeClass('filtered');
                    } else {
                        // loop through the rows
                        for (j = 0; j < l; j++) {
                            // skip child rows
                            if (reg[1].test($tr[j].className)) { continue; }
                            r = true;
                            cr = $tr.eq(j).nextUntil('tr:not(.' + c.cssChildRow + ')');
                            $td = $tr.eq(j).add(cr).children('td');
                            for (i = 0; i < v.length; i++) {
                                // ignore if filter is empty or disabled
                                if (v[i]) {
                                    // check if column data should be from the cell or from parsed data
                                    if (wo.filter_useParsedData || parsed[i]) {
                                        x = c.cache[k].normalized[j][i];
                                    } else {
                                    // using older or original tablesorter
                                        x = $.trim($td.eq(i).text());
                                    }
                                    xi = !reg[2].test(typeof x) && wo.filter_ignoreCase ? x.toLocaleLowerCase() : x;
                                    ff = r; // if r is true, show that row
                                    // val = case insensitive, v[i] = case sensitive
                                    val = wo.filter_ignoreCase ? v[i].toLocaleLowerCase() : v[i];
                                    if (reg[0].test(val)) {
                                        rg = reg[0].exec(val);
                                        try {
                                            ff = new RegExp(rg[1], rg[2]).test(xi);
                                        } catch (err) {
                                            ff = false;
                                        }
                                    // Look for quotes or equals to get an exact match
                                    } else if (reg[3].test(val) && xi === val.replace(reg[4], '')) {
                                        ff = true;
                                    // Look for a not match
                                    } else if (/^\!/.test(val)) {
                                        val = val.replace('!','');
                                        s = xi.search($.trim(val));
                                        ff = val === '' ? true : !(wo.filter_startsWith ? s === 0 : s >= 0);
                                    // Look for operators >, >=, < or <=
                                    } else if (/^[<>]=?/.test(val)) {
                                        rg = $.tablesorter.formatFloat(xi.replace(reg[5], ''), table);
                                        if (isNaN(rg)) { rg = xi; }
                                        s = $.tablesorter.formatFloat(val.replace(reg[5], '').replace(reg[6],''), table);
                                        if (/>/.test(val)) { ff = />=/.test(val) ? rg >= s : rg > s; }
                                        if (/</.test(val)) { ff = /<=/.test(val) ? rg <= s : rg < s; }
                                    // Look for wild card: ? = single, or * = multiple
                                    } else if (/[\?|\*]/.test(val)) {
                                        ff = new RegExp( val.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(xi);
                                    // Look for match, and add child row data for matching
                                    } else {
                                        x = (xi + t).indexOf(val);
                                        ff  = ( (!wo.filter_startsWith && x >= 0) || (wo.filter_startsWith && x === 0) );
                                    }
                                    r = (ff) ? (r ? true : false) : false;
                                }
                            }
                            $tr[j].style.display = (r ? '' : 'none');
                            $tr.eq(j)[r ? 'removeClass' : 'addClass']('filtered');
                            if (cr.length) { cr[r ? 'show' : 'hide'](); }
                        }
                    }
                    $tb.show();
                }

                last = cv; // save last search
                if (c.debug) {
                    ts.benchmark("Completed filter widget search", time);
                }
                $t.trigger('applyWidgets'); // make sure zebra widget is applied
                $t.trigger('filterEnd');
            };

            if (c.debug) {
                time = new Date();
            }
            wo.filter_ignoreCase = wo.filter_ignoreCase !== false; // set default filter_ignoreCase to true
            wo.filter_useParsedData = wo.filter_useParsedData === true; // default is false
            $t
            .bind('addRows updateCell update appendCache search'.split(' ').join('.tsfilter') + '.tsfilter', function(e, filter){
                findRows(e.type === 'search' ? filter : '');
            })
            .find('.tablesorter-filter-row')
            .find('input')
            .addClass(css)
            .bind('keyup search', function(e, filter){
                // ignore arrow and meta keys; allow backspace
                if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; }
                // skip delay
                if (typeof filter !== 'undefined') {
                    findRows(filter);
                    return false;
                }
                // delay filtering
                clearTimeout(timer);
                timer = setTimeout(function(){
                    findRows();
                }, wo.filter_searchDelay || 300);
            });

            // reset button/link
            if (wo.filter_reset && $(wo.filter_reset).length) {
                $(wo.filter_reset).bind('click', function(){
                    $t.find('.' + css).val('');
                    findRows();
                    return false;
                });
            }

            if (c.debug) {
                ts.benchmark("Applying Filter widget", time);
            }
        }
    }
});
smu2015 commented Jul 30, 2012

Hi :)
Thank a lot for your help.
I've tried to use the code.
i have make a fiddle, i have just change HTML and class for childRow :
http://jsfiddle.net/smu2015/D6HHf/5/

But i don't know if it's me but data-column don't seem work.
They are a problem too with a keyword. In your example, if i write 'defined' on your "Order #:" filter, i have all result.

Very thank for your time

Edit : Change icon

Owner
Mottie commented Aug 1, 2012

I've been too busy to work on this, but I should have it for you soon.

Owner
Mottie commented Sep 27, 2012

The filter widget should properly target columns when multiple header rows with column and row spans are included in version 2.4. Please test it out for me. Thanks!

@Mottie Mottie closed this Sep 27, 2012
smu2015 commented Oct 1, 2012

Hi !
I'm currently testing the version 2.4.
/clap for your saveSort, very cool.
i'm looking for multiple rows like you show me in http://jsfiddle.net/eY8uH/80/ but i didn't see the option in 2.4
Can you help me out plz ?

Thank you

Owner
Mottie commented Oct 1, 2012

Hi smu2015!

Here, I've updated the demo with the new theme option.

Basically, you'll need to copy the filter widget code from that demo. I didn't make the filter widget work on multiple rows by default. It's already so huge that I thought I'd wait until I got version 3 done. Then you can build your own plugin.

@Mottie Mottie reopened this Oct 1, 2012
smu2015 commented Oct 1, 2012

Thank you it works, though I've noted a small defect: when I enter the letters composing the key word "undefined" in a search field, all records are returned.

Owner
Mottie commented Oct 2, 2012

Hmm, ok I'll look into that. Thanks!

@Mottie Mottie modified the milestone: Abelt Jan 20, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment