-
29 Jan 2010 4:02 PM #1
Making a speedy combo box with filters?
Making a speedy combo box with filters?
Well in a program I am trying to make, it contains 200 items per combo box.
The combobox could be filtered to display the items of that certain category and to force selection of that category only.
The combobox however is having a big issue with the speed. It takes too long for the list to generate out.
Is there a better way to code the combobox to allow for filters and the input part can input multiple methods like
261-123 / 261123 / 261-123 - The super style
So the real value is 261-123 to submit but the form will take the other 2 inputs and match it to 261-123.
Code:package com.ottocap.NewWorkFlow.client.Widget; import java.util.ArrayList; import java.util.List; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.store.Store; import com.extjs.gxt.ui.client.store.StoreFilter; import com.extjs.gxt.ui.client.widget.form.ComboBox; public class OtherComboBox extends ComboBox<OtherComboBoxModelData> { private ArrayList<String> searchfields = new ArrayList<String>(); private ArrayList<StoreFilter<OtherComboBoxModelData>> myfilters = new ArrayList<StoreFilter<OtherComboBoxModelData>>(); private OtherComboBoxModelData currentvalue; private boolean doforce = false; public OtherComboBox() { this.setDisplayField("name"); this.setQueryDelay(0); searchfields.add("name"); searchfields.add("value"); } public void addSearchField(String field) { searchfields.add(field); } private void fixFilter() { List<StoreFilter<OtherComboBoxModelData>> thefilters = store.getFilters(); if (thefilters != null) { thefilters.clear(); thefilters.addAll(myfilters); } else { for (StoreFilter<OtherComboBoxModelData> thestore : myfilters) { store.addFilter(thestore); } } } public void changeStore(ListStore<OtherComboBoxModelData> store, String value) { this.setStore(store); this.getListView().setStore(store); this.setValueFull(value); } @Override public void doQuery(String q, boolean forceAll) { if (forceAll) { super.doQuery("", false); } else { super.doQuery(q, false); } } @Override protected void doAttachChildren() { super.doAttachChildren(); } public void addFilter(final String field, final Object fieldvalue) { StoreFilter<OtherComboBoxModelData> myfilter = new StoreFilter<OtherComboBoxModelData>() { @Override public boolean select(Store<OtherComboBoxModelData> store, OtherComboBoxModelData parent, OtherComboBoxModelData item, String property) { return item.get(field).equals(fieldvalue); } }; myfilters.add(myfilter); store.clearFilters(); fixFilter(); store.filter(this.getDisplayField()); } public void clearFitler() { myfilters.clear(); store.clearFilters(); fixFilter(); store.filter(this.getDisplayField()); } @Override protected void onFocus(ComponentEvent ce) { boolean selected = mimicing; super.onFocus(ce); if (!selected) { store.clearFilters(); fixFilter(); store.filter(this.getDisplayField()); } } @Override public OtherComboBoxModelData getValue() { // return super.getValue(); if (!rendered) { return currentvalue; } else { return searchValue(getRawValue()); } } @Override protected OtherComboBoxModelData findModel(String property, String value) { if (value == null || store == null) return null; for (int i = 0; i < store.getCount(); i++) { if (value.toLowerCase().equals(((String) store.getAt(i).get(property)).toLowerCase())) { return store.getAt(i); } } return null; } @Override public void setValue(OtherComboBoxModelData value) { currentvalue = value; super.setValue(value); } @Override public void setForceSelection(boolean forceSelection) { doforce = forceSelection; } @Override public boolean getForceSelection() { return doforce; } public void setValue(String value) { setValue(searchValue(value)); } private void setValueFull(String value) { currentvalue = null; setValue(searchValue(value)); } public boolean isOther() { return (getRawValue() != null && getRawValue().startsWith("Other: ")); } public OtherComboBoxModelData searchValue(String findvalue) { if (findvalue == null || (findvalue.trim().equals("") && !getForceSelection())) { currentvalue = null; return null; } if (currentvalue != null && currentvalue.getName().equals(getRawValue())) { return currentvalue; } if (store != null) { store.clearFilters(); fixFilter(); store.filter(this.getDisplayField()); } if (findvalue.startsWith("Other: ")) { findvalue = findvalue.substring("Other: ".length()); } OtherComboBoxModelData foundvalue = null; searchloop: for (int i = 0; i < searchfields.size(); i++) { if (foundvalue == null) { foundvalue = findModel(searchfields.get(i), findvalue); } if (foundvalue != null) { currentvalue = foundvalue; setRawValue(foundvalue.getName()); break searchloop; } } if (getForceSelection()) { if (foundvalue == null || findvalue.equals("")) { if (getStore() != null && getStore().getCount() == 1) { foundvalue = getStore().getModels().get(0); currentvalue = foundvalue; setRawValue(foundvalue.getName()); return foundvalue; } else { setRawValue((currentvalue != null) ? currentvalue.getName() : ""); return currentvalue; } } } else { if (foundvalue == null && !findvalue.equals("")) { setRawValue("Other: " + findvaluename(findvalue)); foundvalue = new OtherComboBoxModelData("Other: " + findvaluename(findvalue), findvalue); currentvalue = foundvalue; } } return foundvalue; } protected String findvaluename(String valuetext) { return valuetext; } }
-
1 Feb 2010 8:26 AM #2
interesting.
1) you have also the code for "OtherComboBoxModelData"?
2) you have also a small codesnippet how to use it?This forum needs your help: you got hints from the community and now you have fixed your code? dont just reply with "now its fixed" or "i found the error"! please take the time to post also an detailed answer with the working code.
GreaseMonkey Script for a GXT-only Forum: it hides ExtJs here: New Posts • Search Results • Advanced Search form • Category overview http://www.extjs.com/forum/showthrea...041#post410041
-
1 Feb 2010 9:46 AM #3
the other combobox model data is
However the main problem is my code is too slow. I am trying to figure out a fast way to do this.Code:package com.ottocap.NewWorkFlow.client.Widget; import com.extjs.gxt.ui.client.data.BaseModelData; public class OtherComboBoxModelData extends BaseModelData { /** * */ private static final long serialVersionUID = 1L; public OtherComboBoxModelData() { } public OtherComboBoxModelData(String name, String value) { this.set("name", name); this.set("value", value); } public OtherComboBoxModelData(String name) { this(name,name); } public String getName() { return this.get("name"); } public String getValue() { return this.get("value"); } }
The thing is it is suppose to be like a combobox however the list is was suppose to be filterable and it matches with a value and not the name.
However the main problem is that I could not generate the object I want to match it with by a string.
There are 3 things I could match:
The name of the object.
The value of the object.
And extra values of a different map.
The filter was to make the list display stuff in a category only and if force selection was on, it will only force to the values of that category. However this part could just be set to another store.
Main thing I am trying to do I guess is to make a combobox like a select box where name and value are different but it can still get to the name. Also if you put in a value that isnt on the list it will show as "Other: Going Somewhere"
-
1 Feb 2010 1:21 PM #4
Well here is my new code that might be a bit faster .
The rendering of the choices is still a bit slow however.Code:package com.ottocap.NewWorkFlow.client.Widget; import java.util.ArrayList; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.widget.form.ComboBox; public class OtherComboBox extends ComboBox<OtherComboBoxModelData> { private ArrayList<String> searchfields = new ArrayList<String>(); public OtherComboBox() { this.setDisplayField("name"); this.setQueryDelay(0); this.setTriggerAction(TriggerAction.ALL); searchfields.add("name"); searchfields.add("value"); } @Override protected void triggerBlur(ComponentEvent ce) { if (this.getValue() == null || !this.getValue().getName().equals(this.getRawValue())) { this.setValue(getRawValue()); } super.triggerBlur(ce); } private OtherComboBoxModelData matchValue(String value) { if (value == null) { return null; } else { value = value.toLowerCase(); } for (OtherComboBoxModelData model : store.getModels()) { for (int i = 0; i < searchfields.size(); i++) { if (value.equals(((String) model.get(searchfields.get(i))).toLowerCase())) { return model; } } } return null; } public void setValue(String value) { OtherComboBoxModelData thevalue = null; if ((value == null || value.equals("")) && !getForceSelection()) { this.setValue(thevalue); return; } if (value != null && value.startsWith("Other: ")) { value = value.substring("Other: ".length()); } if (store != null) { thevalue = matchValue(value); } if ((store == null || thevalue == null) && !getForceSelection()) { thevalue = new OtherComboBoxModelData("Other: " + value, value); } if (thevalue == null && getForceSelection()) { this.setRawValue(""); return; } this.setValue(thevalue); } public void changeStore(ListStore<OtherComboBoxModelData> thestore, String value) { this.setStore(thestore); this.getListView().setStore(thestore); if (getForceSelection()) { setValue(matchValue(value)); } else { setValue(value); } } public boolean isOther() { return getValue().getName().startsWith("Other: "); } public void addSearchField(String field) { searchfields.add(field); } }
-
1 Feb 2010 3:52 PM #5
right now the slowest part seems to be the listview that displays. the full list seems to be too slow when it is big. If there was a way to just cache the full list that would be nice. so it does not have to regenerate it which takes a while.
-
2 Feb 2010 12:36 AM #6
For so many entries, perhaps you should consider an alternative to the combo box - I think it is bad UI design to use such large combo boxes.
Matt.
-
2 Feb 2010 12:57 AM #7
but that is what they want. it is a custom hat program. there is like 100 + styles to choose from.
it is a really complex program.
The only way i can think of making it even faster is if there are multiple pages but that slows down their design of the custom cap.
my old program written in gwt worked fine with a single list box where i move around to the part that they want to select. But they wanted a combobox and some changes that caused me have to rewrite the whole program. I used gxt out of the different choices because I found it the best lib. The main problem that is slowing down the program is the combobox list when it is fully populated to show.
If there was some kind of lazy rendering of the popup list objects that might make it faster.
the popup list right now ahve a lazy render where it renders the list when u click on it. That helps the page load speed but it doesnt help when you click on the list when the list have a lot of items.
need like a paging drop down list for it or something to help.
-
24 Feb 2010 9:18 AM #8
-
24 Feb 2010 9:25 AM #9
-
24 Feb 2010 9:47 AM #10
ya thats what i did, changed it to a paging type combobox using the RpcProxy but the datasource is local.
this is the store i use however it seems like i have to generate a new store for each combobox and not just have 1 shared because the store bindings seems to transfer to other paging comboboxes when i view the list.Code:public ListStore<OtherComboBoxModelData> getPagingStore() { RpcProxy<BasePagingLoadResult<OtherComboBoxModelData>> proxy = new RpcProxy<BasePagingLoadResult<OtherComboBoxModelData>>() { @Override protected void load(Object loadConfig, AsyncCallback<BasePagingLoadResult<OtherComboBoxModelData>> callback) { PagingLoadConfig loadconfig = (PagingLoadConfig) loadConfig; ArrayList<OtherComboBoxModelData> fulldata = loadconfig.get("datasource"); String currentsearch = loadconfig.get("query"); ArrayList<String> searchfields = loadconfig.get("searchfields"); FastMap<String> remotefilter = loadconfig.get("remotefilter"); currentsearch = currentsearch.toLowerCase(); String[] keyset = remotefilter.keySet().toArray(new String[] {}); int matchcounter = 0; int offsetcount = 0; ArrayList<OtherComboBoxModelData> partdata = new ArrayList<OtherComboBoxModelData>(); for (int i = 0; i < fulldata.size(); i++) { for (int j = 0; j < searchfields.size(); j++) { boolean passfilter = true; for (int k = 0; k < keyset.length; k++) { if (!remotefilter.get(keyset[k]).equals(fulldata.get(i).get(keyset[k]))) { passfilter = false; } } if (passfilter) { if (((String) fulldata.get(i).get(searchfields.get(j))).toLowerCase().contains(currentsearch)) { if (offsetcount < loadconfig.getOffset()) { offsetcount++; matchcounter++; } else if (partdata.size() < loadconfig.getLimit()) { matchcounter++; partdata.add(fulldata.get(i)); } else { matchcounter++; } j = searchfields.size(); } } } } callback.onSuccess(new BasePagingLoadResult<OtherComboBoxModelData>(partdata, loadconfig.getOffset(), matchcounter)); } }; BasePagingLoader<BasePagingLoadResult<OtherComboBoxModelData>> loader = new BasePagingLoader<BasePagingLoadResult<OtherComboBoxModelData>>(proxy); return new ListStore<OtherComboBoxModelData>(loader); }
The problem is that now it is in multiple pages. and not just a single long list. The vaaldin method for a combobox using a bufferedview seem to be the best by not rendering the list elements until scroll to.


Reply With Quote