Reputation: 130
I am creating a SearchAsYouType Combobox tied to a Store. It features tokenizing the input into words and match it (for now) against all properties of an Object:
{"id": "id_someid",
"lastName": "Porker",
"firstName": "Peter"}
I decided to create the Filters on the fly when the input of the combobox changes:
Ext.define('MyApp.controller.SearchFilterController', {
extend: 'Ext.app.Controller',
views: [
'SearchAsYouTypeCombo'
],
configureFiltersBeforeQuery: function(queryPlan) {
//add filter per word token
comboBox = queryPlan.combo;
queryString = queryPlan.query;
words = queryString.split(/[,\s]+/);
myStore = comboBox.getStore();
myStore.clearFilter(false); //I also tried without this line, did not help
if(!Ext.isEmpty(queryString))
{
//create a filter per word
filters = [];
Ext.Array.each(words,
function(word, index, wordsArray){
console.log('NEW FILTER: word: ' + word);
filterAllPropertiesFilter =
Ext.create('Ext.util.Filter',
{ filterFn: function(item){
match = false;
Ext.Object.each(item.data,
function (property, value){
//match beginning of word
match = match || (String(value).toLowerCase().lastIndexOf(word.toLowerCase(), 0) === 0);
return !match; //do not check other properties when match is found
});
return match;
}});
filters.push(filterAllPropertiesFilter);
return true;
},
this,
false);
myStore.addFilter(filters);
}
return true;
},
init: function(application) {
this.control({
'SearchAsYouTypeCombo': {
beforequery: this.configureFiltersBeforeQuery
}
});
}
});
EDIT: There is a "Kevin Bacon" in my Store. Let's say that none of the LastNames of the other Persons in the local Store starts with "Ba", but there is someone else whose firstName is "Barry". So when I type in the search query "Ba" I want to see "Kevin Bacon" and "Barry White" as results. This works.
But here is what I cannot accomplish: When I now go on extending the searchString to "Ba, Ke" I don't want the code to interate over all Persons in my Store again when applying the filter. I want the upcoming filter to only check against the left two results from the previous filter. I kinda want to apply a filter upon another filter.
Upvotes: 4
Views: 8817
Reputation: 25001
You won't be able to accomplish what you want using the store's filters because these methods all end up filtering on the whole dataset. So you'll need to apply your filter yourself!
In order to do that, we need to replicate the code from Ext.data.Store#filter
, except for the part that restores the whole dataset before filtering.
That gives us:
// -- Ensure that our current filter is not stalled ----------------
// Clear our filtering if the query string has changed in a way
// that invalidate the current filtering
if (
// previous filter is stalled
) {
myStore.data = myStore.snapshot;
delete myStore.snapshot;
}
// -- Create your new or updated filter ----------------------------
var filter = new Ext.util.Filter({
filterFn: function(record) {
// your filtering logic
}
});
// -- Apply the filter to the currently filtered data --------------
myStore.snapshot = myStore.snapshot || myStore.data.clone();
// !!!
// Here's the essential difference. We filter from the filtered dataset:
myStore.data = myStore.data.filter(myFilter);
// Instead of starting again from the unfiltered snapshot like in the original code:
//me.data = me.snapshot.filter(filters);
myStore.constructGroups();
if (myStore.sorters.length && myStore.sortOnFilter && !myStore.remoteSort) {
myStore.sort();
} else {
// fire datachanged event if it hasn't already been fired by doSort
myStore.fireEvent('datachanged', myStore);
myStore.fireEvent('refresh', myStore);
}
// This line's probably useless since filters haven't actually changed...
//myStore.fireEvent('filterchange', myStore, myStore.filters.items);
As I've hinted in my previous comment, you'll obviously need a way to detect when the query string has changed in a way that render the current filtering outdated. I've added a mock condition at the beginning of the code to show you how to clear this hacky filtering when that happens.
Upvotes: 2