PDA

View Full Version : Example: Optimization for Remote Combo



durlabh
23 May 2008, 1:05 PM
When we are using remote combo stores with paging, after each key press the store is queried. For example, let us say you have following values in your database:

John
Johnny
Johnson

Now, once you type John, the store will be queried. Now, if we press "n" again, we know that there are only 3 entries matching "John" so we shouldn't be querying the back-end again. Right now, at-least upto 2.0.2, the default behavior is to requery. Now, if the user queries backspace, the value will still be "John" and we already know that there are only 3 records in the database and that was the last query. So, we can avoid requery. To handle this, I made the following changes:



Ext.override(Ext.form.ComboBox, {
doQuery : function(q, forceAll){
if(q === undefined || q === null){
q = '';
}
var qe = {
query: q,
forceAll: forceAll,
combo: this,
cancel:false
};
if(this.fireEvent('beforequery', qe)===false || qe.cancel){
return false;
}
q = qe.query;
forceAll = qe.forceAll;
if(forceAll === true || (q.length >= this.minChars)){
if(this.lastQuery !== q){
// change: Durlabh's change start
var lastQuery = this.lastQuery;
// change:
this.lastQuery = q;
if(this.mode == 'local'){
this.selectedIndex = -1;
if(forceAll){
this.store.clearFilter();
}else{
this.store.filter(this.displayField, q);
}
this.onLoad();
}else{
// change: Durlabh's change start
var storeCount;
if(this.store.snapshot) {
storeCount = this.store.snapshot.length;
} else {
storeCount = this.store.getCount();
}
if(this.store.lastOptions && this.store.lastOptions.params) {
var lastQuery = this.store.lastOptions.params[this.queryParam];
}
if(lastQuery != null
&& q.length >= lastQuery.length
&& q.substr(0, lastQuery.length) == lastQuery
&& this.store.getTotalCount() == storeCount) {

this.selectedIndex = -1;
this.store.filter(this.displayField, q);
this.onLoad();
}
else {
this.store.baseParams[this.queryParam] = q;
this.store.load({
params: this.getParams(q)
});
}
// change:
this.expand();
}
}else{
this.selectedIndex = -1;
this.onLoad();
}
}
}
});


Let me know what do you think of this? Any bugs/ suggestions?

fangzhouxing
23 May 2008, 8:10 PM
Great! I will try it in my project.

fangzhouxing
29 May 2008, 10:56 PM
I have tested it, it is very nice. durlabh, Thank you again!

rnfbr1
30 Nov 2008, 11:37 PM
Excellent work !!! , as an added advantage let me tell you that i was having a problem with
combobox autocompletion , it just would not work no matter what i did ,but your override took care of business
and now it works flawlessly . Thank you very much for your contributions and by the way
your site is great.

skaue
3 Dec 2008, 12:16 AM
I already have this extended to let the autocompleter search the entire string for local stores. Like if you wrote "ate" you would get hits on "Kate", "Mate" or "schwaccate".. ;)

How would I go about to combine these two enhancements?
This is the extension to let the query search the entire string when autocompleting:


Ext.override(Ext.form.ComboBox, {
doQuery: function(q, forceAll)
{
if (q === undefined || q === null)
q = '';

var qe = {
query: q,
forceAll: forceAll,
combo: this,
cancel: false
};
if (this.fireEvent('beforequery', qe) === false || qe.cancel)
return false;

q = qe.query;
forceAll = qe.forceAll;
if (forceAll === true || (q.length >= this.minChars))
{
if (this.lastQuery !== q)
{
this.lastQuery = q;
if (this.mode == 'local')
{
this.selectedIndex = -1;
if (forceAll)
{
this.store.clearFilter();
} else
{
this.store.filter(this.displayField, q, true); // overridden
}
this.onLoad();
} else
{
this.store.baseParams[this.queryParam] = q;
this.store.load({
params: this.getParams(q)
});
this.expand();
}
} else
{
this.selectedIndex = -1;
this.onLoad();
}
}
}
});

galdaka
8 Dec 2008, 8:28 AM
I already have this extended to let the autocompleter search the entire string for local stores. Like if you wrote "ate" you would get hits on "Kate", "Mate" or "schwaccate".. ;)

How would I go about to combine these two enhancements?
This is the extension to let the query search the entire string when autocompleting:


Ext.override(Ext.form.ComboBox, {
doQuery: function(q, forceAll)
{
if (q === undefined || q === null)
q = '';

var qe = {
query: q,
forceAll: forceAll,
combo: this,
cancel: false
};
if (this.fireEvent('beforequery', qe) === false || qe.cancel)
return false;

q = qe.query;
forceAll = qe.forceAll;
if (forceAll === true || (q.length >= this.minChars))
{
if (this.lastQuery !== q)
{
this.lastQuery = q;
if (this.mode == 'local')
{
this.selectedIndex = -1;
if (forceAll)
{
this.store.clearFilter();
} else
{
this.store.filter(this.displayField, q, true); // overridden
}
this.onLoad();
} else
{
this.store.baseParams[this.queryParam] = q;
this.store.load({
params: this.getParams(q)
});
this.expand();
}
} else
{
this.selectedIndex = -1;
this.onLoad();
}
}
}
});


Live example?

The latest code of this widget with your modification is in: http://www.jadacosta.es/extjs/examples/boxselect/index.html

Any solution for posibility enter "free values"?

Thanks in adfvance,

skaue
8 Dec 2008, 8:34 AM
Live example?

The latest code of this widget with your modification is in: http://www.jadacosta.es/extjs/examples/boxselect/index.html

Any solution for posibility enter "free values"?

Thanks in adfvance,

Simply add the override after declaration of Combobox (after ext-all.js), and before BoxSelect plugin. In other words, make sure you include your scripts in the following order:

ext-base
ext-all
ext-override (like this combobox override)
plugins (like the boxselect-plugin)


Its better to discuss this back in the boxselect thread, and not in this thread, as it is "off-topic"... ok? ;)

durlabh
1 May 2009, 5:59 AM
I'm now using a slightly modified version of this enhancement for my auto-complete text boxes. The code below is specifically being used with SimpleStore:



Ext.ux.AutoComplete = Ext.extend(Ext.form.ComboBox, {
mode: 'remote',
displayField: 'text',
valueField: 'text',
forceSelection: false,
minChars: 1,
hideTrigger: true,
queryDelay: 0,
limit: 10, // new property as threshold for requery
loadingText: null,
initComponent: function() {
if (!this.store) {
this.store = new Ext.data.SimpleStore({
fields: ['text'],
url: 'controllers/Combo.aspx',
baseParams: { comboType: this.comboType, limit: this.limit }
});
}
Ext.ux.AutoComplete.superclass.initComponent.call(this);
},
onBeforeLoad: function(store, options) {
if (!this.hasFocus) {
return;
}
if (store.lastOptions) {
var q = store.baseParams[this.queryParam];
var lastQuery = store.lastOptions.params[this.queryParam];

var useOldStore = false;
if (q.length >= lastQuery.length && q.substr(0, lastQuery.length) == lastQuery) {
if (store.totalProperty) {
var storeCount;
if (store.snapshot) {
storeCount = store.snapshot.length;
} else {
storeCount = store.getCount();
}
useOldStore = store.getTotalCount() == storeCount;
} else {
useOldStore = !this.limit || store.getTotalCount() < this.limit;
}
if (useOldStore) {
this.selectedIndex = -1;
this.store.filter(this.displayField, q);
this.onLoad.defer(50, this);
return false;
}
}
}
this.selectedIndex = -1;
}
});
Ext.reg("autocomplete", Ext.ux.AutoComplete);


The major change from my previous implementation is the fact that I'm overriding the onBeforeLoad method instead of doQuery. Since original onBeforeLoad method wasn't doing much, so I found this to be much cleaner. Also, I don't need to check for remote/ local combo anymore. Also, I've tested this to be working fine with SimpleStore but it should work fine with JsonStore as well.