| Page 1 of 1 | [ 8 posts ] |
|
Maybe I missed it on the site but I didn't notice a client side filtering example (only an example that did a sendrequest for more data and then filtered) so I wrote one and I thought I'd share. Unfortunatey we have some very long (time to retrieve) queries so I had to have this. I did two versions, one if filtering via 1 column at a time and another that filters multiple columns at a time (i.e. text from multiple fields taken into account when filtering.)
The first try at this (not shown in here) the performance was terrible so I kept trying and this seems to be pretty scalable. I tried it with 2000 records and it handled it well with minimal delay. Seems like this method I settled on doesn't matter the total records as much as how many rows displayed (because GUI rendering is always heavier than calculations.) So you could tell your users to do (assuming YUI paginator drop-down) less records on page when filtering. One field filtering of DataTable: Code: myDataTable.filterTable = function(columnName, value) { var recordSet = this.getRecordSet(); //Create a cache of the original record set if(!this.origRecordSet) { this.origRecordSet = new Array(); for(i=0;i<recordSet.getLength();i++) { this.origRecordSet[i] = recordSet.getRecord(i).getData(); } } //Determine if filter fields have any text or not. //skip 1 char or less because we are doing indexOf to filter //and that won't filter much with 1 char //You could do > 0 here if you wanted to do 1 char filtering var allClear = true; if(value.length>1){ allClear = false; } var temp = null; //If no text in any field filters then pass through the original set if(allClear) { temp = this.origRecordSet; }else { //Otherwise, find what we need to filter temp = new Array(); var idx = 0; for(i=0;i<this.origRecordSet.length;i++) { var record = this.origRecordSet[i]; //For one column filter at a time... var dataValue = record[columnName]; if(dataValue && dataValue.toLowerCase().indexOf(value)>=0){ temp[idx] = record; ++idx; } } } this.deleteRows(0, recordSet.getLength()); this.addRows(temp); }; myDataTable.doBeforeSortColumn = function(oColumn , sSortDir) { this.origRecordSet = null; //Order changed so we need to refresh our "cache" }; myDataTable.doBeforeLoadData = function(oColumn , sSortDir) { this.origRecordSet = null; //New data so we need to refresh our "cache" }; <input id='stateFilter' type='textfield' name='stateFilter' title='StateFilter' onkeyup='myDataTable.filterTable("state", this.value)'/> Multi-field filtering of DataTable: Code: myDataTable.filterTable = function(values) { //takes an array of strings to filter multiple cols var recordSet = this.getRecordSet(); //Create a cache of the original record data if(!this.origRecordSet) { this.origRecordSet = new Array(); for(i=0;i<recordSet.getLength();i++) { this.origRecordSet[i] = recordSet.getRecord(i).getData(); } } //Determine if filter fields have any text or not to skip processing below if we can. var allClear = true; //if(value.length>1){ allClear = false; } //Next few lines are if trying to filter multiple fields at once for(i=0;i<values.length;i++) { if(values[i].length>=1){ allClear = false; break; } } var temp = null; //If no text in any field filters then pass through the original set //Maybe this helps performance, maybe it doesn't, you decide if(allClear) { temp = this.origRecordSet; }else { //Find what we need to filter temp = new Array(); var idx = 0; for(i=0;i<this.origRecordSet.length;i++) { var record = this.origRecordSet[i]; var allMatch = true; //All column filters have to match to keep record for(j=0;j<this.filterFieldNames.length;j++) { var dataValue = record[this.filterFieldNames[j]]; //The toString() may not be necessary for you. //The example I chose as my base example had an array of colors if(dataValue && dataValue.toString(). toLowerCase().indexOf(values[j])>=0){ //Do nothing here because we match and allMatch==true initially }else { allMatch=false; break; } } if(allMatch) { temp[idx] = record; ++idx; } } } this.deleteRows(0, recordSet.getLength()); this.addRows(temp); }; myDataTable.doBeforeSortColumn = function(oColumn , sSortDir) { this.origRecordSet = null; //Reset order means use new cache }; myDataTable.doBeforeLoadData = function(oColumn , sSortDir) { this.origRecordSet = null; //New data means use new cache }; //The functions and vars below are to get the colums (keys) and values //to filter you could do this many different ways. This is just from my //quick and dirty test example to get it working. myDataTable.getFilterValues = function() { //Get from your textfields the values to filter var fieldValues = new Array(); fieldValues[0] = document.getElementById("addressFilter").value; fieldValues[1] = document.getElementById("cityFilter").value; fieldValues[2] = document.getElementById("stateFilter").value; fieldValues[3] = document.getElementById("activeFilter").value; fieldValues[4] = document.getElementById("colorsFilter").value; return fieldValues; }; myDataTable.getFilterFields = function() { var fieldNames = new Array(); fieldNames[0] = "address"; //these values are keys in the columnDefs fieldNames[1] = "city"; fieldNames[2] = "state"; fieldNames[3] = "active"; fieldNames[4] = "colors"; return fieldNames; }; myDataTable.filterFieldNames = myDataTable.getFilterFields(); <input id='stateFilter' type='textfield' name='stateFilter' title='StateFilter' onkeyup='myDataTable.filterTable(myDataTable.getFilterValues())'/> I'm a Java programmer so I may not have written the best JavaScript so any comments are welcome. |
|
Sorry, my test example had all lowercase values and the above code worked. For real data you'll need to do a toLowerCase() on the other value in the compare...
Code: if(dataValue && dataValue.toLowerCase().indexOf(value.toLowerCase())>=0){... and the same for the second example as well. |
|
I found a couple bugs in case someone is interested in using this code.
1. I tried to do the doBeforeSortColumn() override and just set the origRecordSet to null so the data would be reset and then sorted automatically by YUI but that causes a problem when you sort from a filtered view. So what the override of doBeforeSortColumn() should do is sort origRecordSet by the same column and order the record data being displayed is sorted by. I'll have to figure out how to do that. 2. When filtering by a value that returns no records there is a javascript error displayed. It works, shows no values but has an error in the internal YUI code. Seems like a simple check by the YUI code for null or undefined before trying to get a value from an internal array would have helped me out. But that doesn't exist. I get a "r.getPageRecords() is null" javascript error. I'll have to figure out how to suppress that error. |
|
Quote: Maybe I missed it on the site but I didn't notice a client side filtering example (only an example that did a sendrequest for more data and then filtered) ... Hey Joe, Haven't had a chance to look over your posting in detail, but I did notice your comment above. There is a YUI 2 example "Client-side filtering of Local Data" at http://developer.yahoo.com/yui/examples/datatable/dt_localfilter.html that appears to be similar. You reference in your posting that an example exists that uses "sendRequest" and you wanted to do it locally, or something like that. The example above uses "local data" (from a local Javascript array) and loads that into DataSource. The ".sendRequest" method is a DataSource method that doesn't actually make a remote call, but makes a call to the DS. The DS determines if the data is "cached" internally, and if not may make a call to the DS source to retrieve it. But if you are setup with a local array, the sendRequest simply retrieves it from the DS cache. It's kind of all relative, but you definitely are not making a "remote" call (i.e. to a different URL) in the above example. Just wanted to throw that out there. Look forward to reviewing your code ... Regards, Todd |
|
Todd, thanks for the response.
I saw that example but the problem is that I am using an XHRDataSource at work (I know in my example I used a LocalDataSource to make it quicker to figure out when working on the problem at home) which means when you do a sendRequest() with an XHRDataSource it will go remote. Unless you had a hybrid DataSource (which I posted about a little bit ago for which I haven't found a good solution) which you can tell it to go remote when you want and then tell it to go local when you want. I played around with trying to use the built in cache too and found it too hard to just manipulate directly at will (couldn't figure out how to get the proper types needed.) Maybe I am missing something there? Let me know. I look forward to any feedback. Thanks. |
|
Joe,
I think I remember seeing your "hybrid" DS post, I thought Alberto had covered that one okay. Regarding this posting, I didn't see a complete example in your initial code snippet (no DataSource or DataTable instantiation). Could you maybe provide a link to a a pastie or gist of the bulk of your code to take a look at? Now regarding your hybrid question. It sounds like you have a bunch of remote data you would initially like to load first, and then use within a DataTable locally, is that correct? Do you want to save changes from the local data back to the remote data server or is the data basically a read-only snapshot? I had worked up an example awhile ago that loads data initially via a separate AJAX call (via Connection Manager) not having anything to do with DataSource or DataTable. Then in the "success" return from Connection Manager I fire off a call to create a DataTable using the received data as a Local Datasource. A live working link to this example is http://blunderalong.com/pub/dtb/dt_remote_json_ajax.html. There is no "filtering" per se on this, but maybe this is close to what you were looking for? Of course changes to the Local DS are not saved to the server, you would need to do more work to incorporate that. For what its worth. If you can post a pastie link to see the remainder of your sanitized code maybe one of the experts here can look it over ... Regards, Todd |
|
Todd, thanks again for the input. I think I figured out what I needed to make it work well. I needed to do my sorting of my "cached" record set in the filter function -- on-demand. Then everything is working well.
I like the way I did it over the filter example on the site because this code will work with any DataSource type (LocalDataSource, XHRDataSource, etc.) and the example on the site, I believe, will only work with a LocalDataTable (because a sendRequest() on an XHRDataSource will go remote and defeats the purpose of local filtering.) Not only that, in the example there is a comment about "//Reset sort" and not sure why you would want to do that? If I sort by "state" column (state is the name of the column in the example) and then filter, I'd probably want to keep sorted by the "state" columm when filtering and not reset like the example. My version just puts the filtered or non-filtered data in the same sort state that is there at a given time. Again, I had to add a few more lines of code than what I posted the other day but it seems to work just like I wanted it. Thanks for helping me think through the development of it. |
|
Cool!, glad you are up and running
|
| Page 1 of 1 | [ 8 posts ] |
| You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum |
© 2006-2013 Yahoo! Inc. All rights reserved.
All code on this site is licensed under the BSD License unless stated otherwise.
About This Site · Security Contact Info
Powered by phpBB® Forum Software © phpBB Group