flitering multiple Datatables without using regex - javascript

I'm using Datatables 1.10.19 though I would happily upgrade if that will help.
I have an application with several tables on the same page. For one of those tables I want to offer the user the option to do a negative search. They enter a value and see all the rows that don't have that value.
So far as I can see the search() API allows simple text or regex criteria. I've seen examples such as this.
var allTables = $('table.dataTable').DataTable();
allTables.column( 0 ).search( 'mySearchTerm', true, false ).draw();
Some regex dialects support negative look ahead so regex such as those described in this answer would allow me to specify negation, but it appears that that the regex engine in use in Datatables does not work with such expressions.
My alternative is to use a filter function which I can establish with this:
$.fn.dataTable.ext.search.push()
However that seems to be a global construct affecting all tables, which I really don't want.
Any suggestions please?

You can use $.fn.dataTable.ext.search.push() - but it requires some additional work, to handle the fact (as you point out) that this is a global function, affecting all tables.
The following is not a full implementation, but shows the main points:
The Approach
My example uses 2 tables, with hard-coded data in each HTML table.
Each table is initialized as follows:
$(document).ready(function() {
// ---- first table with custom search ---------//
var table_a = $('#example_a').DataTable( {
pageLength: 10
} );
// remove the default DT search events - otherwise
// they will always fire before our custom event:
$( "#example_a_filter input" ).unbind();
// add our custom filter event for table_a:
$('#example_a_filter input').keyup( function( e ) {
table_a.draw();
} );
// ---- second table with DT default search ----//
var table_b = $('#example_b').DataTable( {
pageLength: 10
} );
} );
For my custom search function, I make use of the fact that the function includes a settings parameter which we can use to see which table is being searched:
$.fn.dataTable.ext.search.push (
function( settings, searchData, index, rowData, counter ) {
var tableID = settings.nTable.id;
var searchTerm = $('#' + tableID + '_filter input').val()
//console.log( tableID );
//console.log( searchTerm );
//console.log( searchData );
switch(tableID) {
case 'example_a':
if (searchTerm === '') {
return true;
} else {
show_me = true;
searchData.forEach(function (item, index) {
if (show_me && item.includes(searchTerm)) {
show_me = false;
}
});
return show_me;
}
break;
default:
// for all other tables, pass through the rows
// already filtered by the default DT filter:
return true;
}
}
);
The following line is where we ID which table is being filtered:
var tableID = settings.nTable.id;
After that, I can use a switch statement to handle each table's search separately.
In the default case (for example_b), I'm just passing through what was already filtered by the DT default search:
default: return true;
The above filter looks like this when I search each table for the letter x:
Incomplete Implementation
This logic is incomplete for the custom search logic. It assumes the search term is a single string. If I enter "x y" in the input field, that will exclude all records where a field contains "x y" - which is probably not what you want.
You probably want the exact negation of the standard search - so the input term would need to be split on spaces and each sub-term (x and y) would need to be checked separately across each row of data.

Related

Defining Kendo UI Grid Column Templates with a Function that returns a string

I need to create a Kendo UI for jQuery column which uses a function to determine the template. Essentially the data I am receiving from the backend gives me a number, and I need to match up that number to the corresponding entry in another database table. Once the match is found, I need to set the column template to show that entry.
template: function (e) {
countryData.forEach(function (country) {
let countryDesc;
if (country.countryCode == e.countryCode) {
countryDesc = country.description;
return countryDesc;
}
})
}
This is the function that I have written. countryData is an array of JSON objects containing a list of countries with codes, I am matching that code up with e.countryCode to get the correct country. This is then assigned to countryDesc, and returned.
When ran, the columns are just displayed as 'undefined'. I'm confused as to why this isn't working, as if I do this for example: template: "foo", the column would display foo. Surely all i'm doing is returning a string, so this should work?
So after trying loads of things, replacing the .forEach with a plain for fixed this issue.
template: function (e) {
let country;
for (i = 0; i < countryData.length; i++) {
if (countryData[i].countryCode == e.countryCode) {
country = countryData[i].description;
}
return country;
}

jqGrid how to apply custom filtering / search

I have a jqGrid with a filterToolbar. On one of my columns I want to do a search where for the "filter test", my custom function is called and obviously returns a true for yes it passed of false for no.
Can jqGrid do this, if so, how?
Using the free fork of jqGrid you can use the following
in your colModel add the value searchoptions as so:
{name:'FIELD',width:120,searchoptions:{ sopt: ["cust"] },label:"Field name"}
then add the following property to jqGrid
customSortOperations: {
// the properties of customSortOperations defines new operations
// used in postData.filters.rules items as op peroperty
cust:{
operand: "Custom",// will be displayed in searching Toolbar for example
text: "Custom", // will be shown in Searching Dialog or operation menu in searching toolbar
filter: function (options) {
// The method will be called in case of filtering on the custom operation "cust"
// All parameters of the callback are properties of the only options parameter.
// It has the following properties:
// item - the item of data (exacly like in mydata array)
// cmName - the name of the field by which need be filtered
// searchValue - the filtered value typed in the input field of the searching toolbar
// Get cell data as lowercase
var fieldData = options.item[options.cmName].toLowerCase(),
// Get search terms all in lowercase
terms = $.map(options.searchValue.split(" "), function(val){ return $.trim(val).toLocaleLowerCase(); }),
// A match found
match = false;
// Loop through terms and see if there is a match in the row cell data
$.each(terms, function(i,v)
{
if(fieldData.indexOf(v) != -1)
{
match = true;
// Quit the each loop
return false;
}
});
return match;
}
}
},

Get Devexpress Gridview Sort By and Sort Order in Javascript

I am implementing Devexpress Grid Control in MVC. I was stuck at a point where i need the current Sorted By column and Sort Order (asc/desc) in javascript. I also tried the clientSide Event OnColumnSortingChanged(s , e) , it is only providing the name of the column at click event not from the gridview javascript object.
Got the answer after research...
Have to add CustomJsProperty to the control in the partial view as below
settings.CustomJSProperties = (s, e) =>
{
var Grid = s as MVCxGridView;
var result = new System.Collections.Hashtable();
foreach (var col in Grid.AllColumns)
{
var dataCol = col as GridViewDataColumn;
if (dataCol != null)
{
if (dataCol.SortIndex == 0)
{
e.Properties["cpColumnsProp"] = new Dictionary<string, object>()
{
{ "sortcolumn", dataCol.FieldName },
{ "sortorder", ((Convert.ToString(dataCol.SortOrder).Equals("Ascending")) ? "asc" : "desc")}
};
}
}
}
};
Handle the ColumnSortingChange event as follows
function gvChartList_OnColumnSortingChanged(s, e) {
$("#hdsortname").val(e.column.fieldName); // contains the sort column name
$("#hdsortorder").val(((s.cpColumnsProp.sortcolumn == e.column.fieldName) ? "desc" : "asc")); // contains the sort column order
}
Last but not the least, One must have to define the default sort index and sort order for the column
settings.Columns.Add(col =>
{
// column details
col.SortIndex = 0;
col.SortAscending();
});
I recently needed similar, where I wanted to save the column order, sort order, and filter information; such that a user could create saved views of a grid, and not have to keep re-entering all these preferences.
I found that in an event CustomJSPropeties I could interact with a sender being an ASPxGridView, such that I could grab a needed value from ASPxGridView.SaveClientLayout(). Also useful here can be the FilterExpression, and the VisibileRowCount - if you want to use the filter expression for exporting, but only when the VisibleRowCount is less than a certain threshold (this is the number of rows shown in the bottom pager area)
settings.CustomJSProperties = (s, e) =>
{
ASPxGridView gridView = (ASPxGridView)s;
e.Properties["cpGridFilterExpression"] = gridView.FilterExpression; //Make Filter Expressions Availabe to JavaScript
e.Properties["cpVisibleRowCount"] = gridView.VisibleRowCount; //Make Visible Row Count Availabe to JavaScript
e.Properties["cpLayout"] = gridView.SaveClientLayout(); //Get Layout String and make it available to JavaScript
};
So what does this do? Properties defined in this event are available as properties of the javascript grid view object. So here we grab these values when we can, and place them in a place where we normally cannot access them.
And then, right from JavaScript we can now access GridView.cpLayout (where GridView is the name/id supplied to your grid.)
<script type="text/javascript">
$(document).ready(function () {
$('#saveButton').click(
function () {
var layout = GridView.cpLayout;
//Perform Save...
}
);
});
</script>
To be clear, this "layout" contains sort order, visible column info, and filter information.
Layout:
https://documentation.devexpress.com/#AspNet/CustomDocument4342
https://documentation.devexpress.com/#AspNet/DevExpressWebASPxGridViewASPxGridView_SaveClientLayouttopic
CustomJSProperties:
https://documentation.devexpress.com/#AspNet/DevExpressWebMvcGridViewSettings_CustomJSPropertiestopic
Note: I add this solution here because this is what I found when I was searching for my issue. As one can see, these are similar topics of accessing the AspxGridView (base of) or MVCGridView within a CustomJSProperties event handler.

How to set goog.ui.Autocomplete minimum input to 0

I would like the autocomplete to show the entire list when the input box gets focused (no input is given). Would also like the auto complete to match substrings without having to fiddle with private variables.
At the moment the code is:
autocomplete = goog.ui.ac.createSimpleAutoComplete(
gsa.Game.gameData.teams, team2, false);
matcher=autocomplete.getMatcher();
matcher.useSimilar_=true
autocomplete.setMatcher(matcher);
Similar matches work but have to set a private variable for that (no getter or setter available).
The other one I have not been able to find out; how to show all data when no input is given (like a smart select input). So when the textbox receives focus it'll show all data since there is no filter text given. These are basic things that one would like to configure but can't find it in the API documentation.
You need to create descendants of goog.ui.ac.AutoComplete, goog.ui.ac.ArrayMatcher and goog.ui.ac.InputHandler. Also you will directly create the instance of auto complete object instead of calling goog.ui.ac.createSimpleAutoComplete.
In goog.ui.ac.AutoComplete descendant you assign custom input handler and matcher.
goog.provide('my.ui.ac.AutoComplete');
goog.require('goog.ui.ac.Renderer');
goog.require('my.ui.ac.ArrayMatcher');
goog.require('my.ui.ac.InputHandler');
my.ui.ac.AutoComplete = function(data, input, opt_multi, opt_useSimilar) {
var renderer = new goog.ui.ac.Renderer();
var matcher = new my.ui.ac.ArrayMatcher(data, !opt_useSimilar);
var inputhandler = new my.ui.ac.InputHandler(null, null, !!opt_multi, 300);
goog.ui.ac.AutoComplete.call(this, matcher, renderer, inputhandler);
inputhandler.attachAutoComplete(this);
inputhandler.attachInputs(input);
};
goog.inherits(my.ui.ac.AutoComplete, goog.ui.ac.AutoComplete);
In goog.ui.ac.ArrayMatcher descendant you need to override getPrefixMatches() method, since the default behaviour discards empty strings. So if there is an empty string, we just return the first x rows from the data.
goog.provide('my.ui.ac.ArrayMatcher');
goog.require('goog.ui.ac.ArrayMatcher');
my.ui.ac.ArrayMatcher = function(rows, opt_noSimilar) {
goog.ui.ac.ArrayMatcher.call(this, rows, opt_noSimilar);
};
goog.inherits(my.ui.ac.ArrayMatcher, goog.ui.ac.ArrayMatcher);
my.ui.ac.ArrayMatcher.prototype.getPrefixMatches = function(token, maxMatches) {
if (token == '')
{
// for empty search string, return first maxMatches rows
return this.rows_.slice(0, maxMatches);
}
else
{
return goog.base(this, 'getPrefixMatches', token, maxMatches);
}
};
In goog.ui.ac.InputHandler descendant you need to override processFocus() method, and force to show the autocomplete popup. This can be done by calling update() method with first parameter set to true.
goog.provide('my.ui.ac.InputHandler');
goog.require('goog.ui.ac.InputHandler');
my.ui.ac.InputHandler = function(opt_separators, opt_literals, opt_multi, opt_throttleTime) {
goog.ui.ac.InputHandler.call(this, opt_separators, opt_literals, opt_multi, opt_throttleTime);
};
goog.inherits(my.ui.ac.InputHandler, goog.ui.ac.InputHandler);
my.ui.ac.InputHandler.prototype.processFocus = function(target) {
goog.base(this, 'processFocus', target);
// force the autocomplete popup to show
this.update(true);
};

How to filter out usercreated events in fullcalendar

I have a fullcalendar where I display non-editable events which are collected from a google calendar, or from a database. Then I want to register customer requests for events from the calendar. This works, but I am not able to list only the events that are added by the user.
Any hint on how to do this?
I tried this:
function retrieve_events() {
var rdv=$('#calendar').fullCalendar( 'clientEvents', undefined);
for (i=0; i<=rdv.length-1; i++) {
/*alert(rdv.toSource());*/
alert(rdv[i].title+" id: "+rdv[i].id+" start: "+rdv[i].start+" end:"+rdv[i].end+" heldag:"+rdv[i].allDay);
}
}
The the "undefined" as id, means that I have given all the non-editable events an id, while the new ones haven't got one. But this way I get all events listed, even those without an id. The same happens with null and ''. But using hardcoded id-numbers returns that specific event.
I see from the documentation that there seems to be other ways to get hold of the events I need, by using other criteria like classes. However I cannot figure out how to specify this filter.
I haven't worked with FullCalendar yet nor do I intend to extensively test this, so I cannot guarantee that this will work.
However, why don't you simple test whether rdv[i].id evaluates to false?
Try:
function retrieve_events( ) {
var rdv = $('#calendar').fullCalendar('clientEvents'),
results = [];
for( var i = 0; i < rdv.length; ++i ) {
if( !rdv[i].id ) {
results.push(rdv[i]);
}
}
return results;
}
P.S.: Passing undefined to .fullCalendar() probably is redundant. It would be equivalent to passing only a single variable. I'd guess the second parameter is a type of events that you can filter for, but passing only a single parameter would cause the plugin to return all events. Also, note that !!'' === false.
The internal check whether the second parameter is set is probably similar to this:
$.fn.fullCalendar = function( command ) {
switch( command ) {
// ... some case's
case 'clientEvents':
var filter = arguments[1];
if( !filter ) {
// Retrieve ALL client events
}
else {
// Filter client events
}
break;
// ... some more case's
}
};
This does not compare types. Testing filter === false would only return true, if filter would evaluate to false and is a boolean.
Following are examples of values that evaluate to false. There may be more, but I believe those are all.
undefined
null
0
false
''

Categories

Resources