Filter: External inputs can target multiple columns.

This commit is contained in:
Mottie 2014-10-20 23:21:57 -05:00
parent 3e42e8bb74
commit 8d0ea85880
5 changed files with 126 additions and 22 deletions

View File

@ -59,6 +59,7 @@
<h3><a href="#">Notes</a></h3>
<div>
<ul>
<li>In <span class="version">v2.18.0</span>, the external search data column can be set to multiple columns (<code>data-column="0-2,4,6-7"</code>). Please see the <a href="example-widget-filter-external-inputs.html">Filter Widget External Inputs</a> demo for more details.</li>
<li>In <span class="version">v2.17.8</span>, added a default fuzzy search to the "First Name" column.</li>
<li>This is a demo of the <a href="index.html#widget-filter-external"><code>filter_external</code></a> option (added <span class="version">v2.15</span>).</li>
<li>In <span class="version">v2.15</span>

View File

@ -78,6 +78,18 @@
<h3><a href="#">Notes</a></h3>
<div>
<ul>
<li>As of <span class="version">v2.18.0</span>,
<ul>
<li>An external input <code>data-column</code> can now target multiple columns:
<ul>
<li>When designating a column for an external input, you can now set multiple columns and/or a range of columns (e.g. <code>data-column="0-2,4,6-7"</code>.</li>
<li>This type of search acts essentially the same as when the column is set to <code>"all"</code> in that "range", "notMatch" and "operators" searches are ignored.</li>
<li>The last search performed using either multiple columns or "all" columns will override all other queries; meaning the other inputs won't clear or update (e.g. performing a search for "foo" in an "all" column search input will override any previous searchs, and the input with a <code>data-column="0-2,4,6-7"</code> will not change its value) - this might be confusing to the user if multiple inputs are visible on the page (placeholder text won't help).</li>
<li>The selector in the demo below has two additional options: "Columns 0,3" and "Columns 1-2"; as you can see, switching the column to search between "all", "0,3" and "1-2" does not change the query.</li>
</ul>
</li>
</ul>
</li>
<li>As of <span class="version">v2.15</span>,
<ul>
<li>A new filter widget <a href="index.html#widget-filter-external"><code>filter_external</code> option</a> has been added that essentially does the same thing as the <a href="index.html#function-bindsearch"><code>$.tablesorter.bindSearch</code></a> function.</li>
@ -106,6 +118,8 @@
<!-- select to change data-column attribute of the above input -->
<select class="change-input">
<option value="all">all</option>
<option value="0,3">Columns 0,3</option>
<option value="1-2">Columns 1-2</option>
<option value="0">Rank</option>
<option value="1">First Name</option>
<option value="2">Last Name</option>

View File

@ -60,6 +60,9 @@
// if you set this to false, make sure you perform a search using the second method below
filter_columnFilters : true,
// css class name added to the filter cell (string or array)
filter_cellFilter : '',
// extra css class name(s) applied to the table row containing the filters & the inputs within that row
// this option can either be a string (class applied to all filters) or an array (class applied to indexed filter)
filter_cssFilter : '', // or []
@ -86,6 +89,9 @@
// see the filter widget custom demo for more specifics on how to use this option
filter_functions : null,
// hide filter row when table is empty
filter_hideEmpty : true,
// if true, filters are collapsed initially, but can be revealed by hovering over the grey bar immediately
// below the header row. Additionally, tabbing through the document will open the filter row when an input gets focus
filter_hideFilters : true,
@ -99,6 +105,9 @@
// a header with a select dropdown & this class name will only show available (visible) options within that drop down.
filter_onlyAvail : 'filter-onlyAvail',
// default placeholder text (overridden by any header "data-placeholder" setting)
filter_placeholder : { search : '', select : '' },
// jQuery selector string of an element used to reset the filters
filter_reset : 'button.reset',
@ -109,6 +118,12 @@
// every character while typing and should make searching large tables faster.
filter_searchDelay : 300,
// allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true
filter_searchFiltered: true,
// include a function to return an array of values to be added to the column filter select
filter_selectSource : null,
// if true, server-side filtering should be performed because client-side filtering will be disabled, but
// the ui and events will still be used.
filter_serversideFiltering : false,
@ -122,7 +137,10 @@
filter_useParsedData : false,
// data attribute in the header cell that contains the default filter value
filter_defaultAttrib : 'data-value'
filter_defaultAttrib : 'data-value',
// filter_selectSource array text left of the separator is added to the option value, right into the option text
filter_selectSourceSeparator : '|'
}
@ -209,6 +227,7 @@ $(function(){
<h3 id="notes"><a href="#">Notes</a></h3>
<div>
<ul>
<li>In <span class="version updated">v2.18.0</span>, added <code>filter_cellFilter</code> &amp; the ability to set multiple "any" match columns for an external search (see the <a href="example-widget-filter-external-inputs.html">external inputs demo</a> for more details).</li>
<li>In <span class="version">v2.17.8</span>, filter selects will default to exact matches unless the header cell has a "filter-match" class added.</li>
<li>In <span class="version">v2.17.1</span>, added a not exact match (<code>!=</code>) filter type.</li>
<li>In <span class="version updated">v2.16+</span>,
@ -285,6 +304,16 @@ $(function(){
<td>if <code>true</code>, a filter will be added to the top of each table column.</td>
</tr>
<tr id="filter-cell-filter">
<td><a href="#" class="permalink">filter_cellFilter</a></td>
<td>''</td>
<td>Extra css class name added to the filter cell (string or array) (<span class="version">v2.18.0</span>)
<div class="collapsible">
<br>
The filter cell (<code>&lt;td&gt;</code>) is not to be confused with the <code>filter_cssFilter</code> option which adds an extra class name to the filter inputs (<code>&lt;input&gt;</code>).
</td>
</tr>
<tr id="filter-css-filter">
<td><a href="#" class="permalink">filter_cssFilter</a></td>
<td>''</td>
@ -561,7 +590,13 @@ $.tablesorter.setFilters( $('table'), [ '', '', '', '', '', '', '11' ], true );<
<h3>Bind External filter</h3>
<blockquote>
Use this method to bind external search filters; include a data-attribute <code>data-column</code> with the column index to target, or use <code>data-column="all"</code> to preform an "any-match" search (<span class="version updated">v2.15</span>). If no <code>data-column</code> is added to the input, the input will be ignored.
Use this method to bind external search filters
<ul>
<li>Include a data-attribute <code>data-column</code> with the column index to target</li>
<li>Or, use <code>data-column="all"</code> to preform an "any-match" search on all of the table columns (<span class="version updated">v2.15</span>).</li>
<li>Or, add multiple columns <code>data-column="0-2,4,6-8"</code> to perform an "any-match" search on selected columns (<span class="version">v2.18.0</span>).</li>
</ul>
If no <code>data-column</code> is added to the input, the input will be ignored.
<pre class="prettyprint lang-html">&lt;!-- will perform an "any-match" type of search (matches any column) --&gt;
&lt;input type="search" class="search" data-column="all"&gt;
&lt;!-- will only search the first column (zero-based index) --&gt;

View File

@ -475,9 +475,9 @@
<li><a href="example-widget-editable.html">Content Editable widget</a> (v2.9; <span class="version updated">v2.13.2</span>).</li>
<li><span class="results">&dagger;</span> Filter Widget (<span class="version updated">v2.17.4</span>):
<ul>
<li><a href="example-widget-filter.html">basic</a> (v2.0.18; <span class="version updated">v2.17.5</span>)</li>
<li><a href="example-widget-filter-any-match.html">external option (match any column)</a> (<span class="version">v2.13.3</span>; <span class="version updated">v2.15</span>)</li>
<li><a href="example-widget-filter-external-inputs.html">external inputs</a> (<span class="version">v2.14</span>; <span class="version updated">v2.15</span>)</li>
<li><a href="example-widget-filter.html">basic</a> (v2.0.18; <span class="version updated">v2.18.0</span>)</li>
<li><a href="example-widget-filter-any-match.html">external option (match any column)</a> (<span class="version">v2.13.3</span>; <span class="version updated">v2.18.0</span>)</li>
<li><a href="example-widget-filter-external-inputs.html">external inputs</a> (<span class="version">v2.14</span>; <span class="version updated">v2.18.0</span>)</li>
<li><a href="example-widget-filter-custom.html">custom</a> (v2.3.6; <span class="version updated">v2.10.1</span>)</li>
<li><a href="example-widget-filter-custom-search.html">custom searches</a> (<span class="version">v2.17.5</span>; <span class="version updated">v2.17.8</span>)</li>
<li>formatter: <a href="example-widget-filter-formatter-1.html">jQuery UI widgets</a> and <a href="example-widget-filter-formatter-2.html">HTML5 Elements</a> (v2.7.7; <span class="version updated">v2.17.5</span>).</li>

View File

@ -877,7 +877,7 @@ ts.filter = {
$ext = wo.filter_$externalFilters;
if (internal !== true) {
// save anyMatch element
wo.filter_$anyMatch = $el.filter('[data-column="all"]');
wo.filter_$anyMatch = $el.filter('[data-column="all"],[data-column*="-"],[data-column*=","]');
if ($ext && $ext.length) {
wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el );
} else {
@ -1040,6 +1040,52 @@ ts.filter = {
}
return val;
},
getLatestSearch: function( $input ) {
return $input.sort(function(a, b) {
return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime');
});
},
multipleColumns: function( c, $input ) {
// look for multiple columns "1-3,4-6,8" in data-column
var ranges, singles, indx,
columns = [],
val = ts.filter.getLatestSearch( $input ).attr('data-column');
// process column range
if ( /-/.test( val ) ) {
ranges = val.match( /(\d+)\s*-\s*(\d+)/g );
$.each(ranges, function(i,v){
var t,
range = v.split( /\s*-\s*/ ),
start = parseInt( range[0], 10 ) || 0,
end = parseInt( range[1], 10 ) || ( c.columns - 1 );
if ( start > end ) { t = start; start = end; end = t; } // swap
if ( end >= c.columns ) { end = c.columns - 1; }
for ( ; start <= end; start++ ) {
columns.push(start);
}
// remove processed range from val
val = val.replace( v, '' );
});
}
// process single columns
if ( /,/.test( val ) ) {
singles = val.split(',');
$.each( singles, function(i,v) {
if (v !== '') {
indx = parseInt( v, 10 );
if ( indx < c.columns ) {
columns.push( indx );
}
}
});
}
if (!columns.length) {
for ( indx = 0; indx < c.columns; indx++ ) {
columns.push( indx );
}
}
return columns;
},
findRows: function(table, filters, combinedFilters) {
if (table.config.lastCombinedFilter === combinedFilters || table.config.widgetOptions.filter_initializing) { return; }
var len, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex,
@ -1112,7 +1158,7 @@ ts.filter = {
}
if ((wo.filter_$anyMatch && wo.filter_$anyMatch.length) || filters[c.columns]) {
data.anyMatchFlag = true;
data.anyMatchFilter = wo.filter_$anyMatch && wo.filter_$anyMatch.val() || filters[c.columns] || '';
data.anyMatchFilter = wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || filters[c.columns] || '';
if (c.sortLocaleCompare) {
// replace accents
data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter);
@ -1142,20 +1188,23 @@ ts.filter = {
data.childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : '';
data.childRowText = wo.filter_ignoreCase ? data.childRowText.toLocaleLowerCase() : data.childRowText;
$cells = $rows.eq(rowIndex).children();
if (data.anyMatchFlag) {
// look for multiple columns "1-3,4-6,8"
columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch );
data.anyMatch = true;
data.rowArray = $cells.map(function(i){
var txt;
if (data.parsed[i]) {
txt = data.cacheArray[i];
} else {
txt = wo.filter_ignoreCase ? $(this).text().toLowerCase() : $(this).text();
if (c.sortLocaleCompare) {
txt = ts.replaceAccents(txt);
if ( $.inArray(i, columnIndex) > -1 ) {
var txt;
if (data.parsed[i]) {
txt = data.cacheArray[i];
} else {
txt = wo.filter_ignoreCase ? $(this).text().toLowerCase() : $(this).text();
if (c.sortLocaleCompare) {
txt = ts.replaceAccents(txt);
}
}
return txt;
}
return txt;
}).get();
data.filter = data.anyMatchFilter;
data.iFilter = data.iAnyMatchFilter;
@ -1468,7 +1517,7 @@ ts.filter = {
};
ts.getFilters = function(table, getRaw, setFilters, skipFirst) {
var i, $filters, $column,
var i, $filters, $column, cols,
filters = false,
c = table ? $(table)[0].config : '',
wo = c ? c.widgetOptions : '';
@ -1485,19 +1534,24 @@ ts.getFilters = function(table, getRaw, setFilters, skipFirst) {
if ($filters && $filters.length) {
filters = setFilters || [];
for (i = 0; i < c.columns + 1; i++) {
$column = $filters.filter('[data-column="' + (i === c.columns ? 'all' : i) + '"]');
// "all" columns can now include a range or set of columms "0-2,4,6-7"
cols = '[data-column' + (i === c.columns ? '="all"],[data-column="any"],[data-column*="-"],[data-column*=",' : '="' + i) + '"]';
$column = $filters.filter(cols);
if ($column.length) {
// move the latest search to the first slot in the array
$column = $column.sort(function(a, b){
return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime');
});
$column = ts.filter.getLatestSearch( $column );
if ($.isArray(setFilters)) {
// skip first (latest input) to maintain cursor position while typing
(skipFirst ? $column.slice(1) : $column).val( setFilters[i] ).trigger('change.tsfilter');
} else {
filters[i] = $column.val() || '';
// don't change the first... it will move the cursor
$column.slice(1).val( filters[i] );
if (i === c.columns) {
// don't update range columns from "all" setting
$column.slice(1).filter('[data-column*="' + $column.attr('data-column') + '"]').val( filters[i] );
} else {
$column.slice(1).val( filters[i] );
}
}
// save any match input dynamically
if (i === c.columns && $column.length) {