Dynamic ComboBox

Hello,
I’m trying to get a text field with a dropdown menu, that contains a certain number of choices. However, since the choices are loaded after the user inserts his/her input ( via an external request ), I can’t seem to find a way to use ComboBox for this purpouse.

Isn’t there an event for ComboBox equal to the TextChangeEvent in the TextField, so that I can put there the code to load my choices?

( There’s an addon that does this, SearchField, but unfortunately I can’t use it as it is not compatible with IE )

Thanks in advance.

look at AbstractSelect.NewItemHandler
see the demo for example : http://demo.vaadin.com/sampler#ComboBoxNewItems

Note: in public void addNewItem(String newItemCaption) {…} You dont have the add the caption as an Item, I use it in some of my applications to build a search query and then add all the matching items to the itemContainer.

Unfortunately that’s not quite what I was looking for.

The problem is that your example allows for a modification of the items after the user has terminated his input, while I need to do it on the fly, WHILE the user is writing.

What I am looking for is a way to get what the user is typing ( as you would from a TextField in immediate mode ), perform my external query, and after that decide what are the choices that the ComboBox will display.

The use case is this:
The user uses a search box (text field or combobox with textfield) and the application has to suggest valid entries based on what the user is typing.
And I cannot provide about 1.5 million options to the constructor of ComboBox. This means I have to know what the user is typing in order to provide suggestions.

Has anyone found a solution to this? I can’t understand why Vaadin doesn’t seem to support this. Every other framework I’ve seen does this. It seems like it’s one of the most basic things for a text field to do.

If you use a container property (instead of toString() etc.) as the ComboBox caption mode, in most cases the actual filtering is delegated to the container. By implementing a suitable lazy container, you should be able to achieve what you want.

If you can afford to keep a part of the data (the set of IDs) in memory, it should be easy to do this by inheriting AbstractInMemoryContainer. If not, maybe you can take e.g. the LazyQueryContainer add-on as a starting point.

I got a similar example working with the JPAContainer (Pro User) but I am not very happy with the results. A performant solution should only require two queries: one to get the count of items and another to load the items. I think because of the way Select is designed it loads by the id so it issues individual queries for each object.

I increased the display size (pageLength) to 25 by subclassing the ComboBox so this results in 25 individual querires rather than 1 query like it does inside a table.

I would actually prefer to use the LazyQueryContainer since we are not in a JPA environment yet (but we are using HIbernate). I was able to get LQC working fine with a table by extending LazyQueryContainer to include filtering support but I’m having trouble using it in the context of the ComboBox.

I used the following articles to help me almost get it working with comboxes:

https://vaadin.com/wiki/-/wiki/Main/Lazy%20Query%20Container/


http://devblog.mycorner.fi/34/using-appfoundation-together-with-lazyquerycontainer/

There seems to be a timing issue with how the Select component adds and removes filters in relation to when container.size() and container.loadBeans() is called. By the time loadBeans() gets called the filter has already been removed.

I have to agree with the other poster, including a lazy combobox example in the demo site does seem like its past due.

Thanks



class FilterableLazyQueryContainer extends LazyQueryContainer implements Container.Filterable {

	private static final long serialVersionUID = 0L

	private static final Logger logger = LoggerFactory.getLogger(FilterableLazyQueryContainer)
	
	boolean applyFiltersImmediately
	
	FilterableLazyQueryContainer(QueryFactory queryFactory, final boolean compositeItems, final int batchSize) {
		super(new FilterableQueryDefinition(compositeItems, batchSize), queryFactory)
	}
	
	@Override
	void addContainerFilter(Filter filter) throws UnsupportedFilterException {
		logger.debug 'Adding container filter: {}', filter
		
		((FilterableQueryDefinition)getQueryView().getQueryDefinition()).addFilter(filter)
		if (applyFiltersImmediately) {
			refresh()
		}
	}

	@Override
	void removeAllContainerFilters() {
		logger.debug 'Removing all container filters'
		
		((FilterableQueryDefinition)getQueryView().getQueryDefinition()).removeAllFilters()
		if (applyFiltersImmediately) {
			refresh()
		}
	}
	
	@Override
	void removeContainerFilter(Filter filter) {
		logger.debug 'Removing container filter: {}', filter
		
		((FilterableQueryDefinition)getQueryView().getQueryDefinition()).removeFilter(filter)
		if (applyFiltersImmediately) {
			refresh()
		}
	}
	
	void applyFilters() {
		refresh()
	}
}

You can see below that the query for the count executes while the filter is in effect but the query which executes for loading the items executes after the filter has been removed which throws off query being executed.



11:26:15.099 [http-bio-8080-exec-10]
 DEBUG c.t.c.v.d.FilterableLazyQueryContainer - Adding container filter: com.vaadin.data.util.filter.SimpleStringFilter@6ac9110
11:26:15.108 [http-bio-8080-exec-10]
 DEBUG c.t.c.v.d.FilterableQueryDefinition - addFilter(com.vaadin.data.util.filter.SimpleStringFilter@6ac9110)
11:26:15.112 [http-bio-8080-exec-10]
 DEBUG c.t.c.d.data.OrganizationInfoQuery - OrganizationInfoQuery(queryDefinition: com.textura.cpms.vaadin.data.FilterableQueryDefinition@6417f586, queryConfiguration: {organizationService=com.textura.cpms.service.app.facade.internal.OrganizationServiceImpl@60f863c0}, sortPropertyIds: [value]
, sortStates: [true]
)
11:26:15.117 [http-bio-8080-exec-10]
 DEBUG c.t.c.d.data.OrganizationInfoQuery - queryCount()
11:26:15.118 [http-bio-8080-exec-10]
 DEBUG c.t.c.v.d.FilterableLazyQueryContainer - Removing container filter: com.vaadin.data.util.filter.SimpleStringFilter@6ac9110
11:26:15.119 [http-bio-8080-exec-10]
 DEBUG c.t.c.v.d.FilterableQueryDefinition - removeFilter(com.vaadin.data.util.filter.SimpleStringFilter@6ac9110)
11:26:15.119 [http-bio-8080-exec-10]
 DEBUG c.t.c.d.data.OrganizationInfoQuery - OrganizationInfoQuery(queryDefinition: com.textura.cpms.vaadin.data.FilterableQueryDefinition@6417f586, queryConfiguration: {organizationService=com.textura.cpms.service.app.facade.internal.OrganizationServiceImpl@60f863c0}, sortPropertyIds: [value]
, sortStates: [true]
)
11:26:15.119 [http-bio-8080-exec-10]
 DEBUG c.t.c.d.data.OrganizationInfoQuery - queryCount()
11:26:15.120 [http-bio-8080-exec-10]
 DEBUG c.t.c.d.data.OrganizationInfoQuery - loadBeans(0, 50)
11:26:15.120 [http-bio-8080-exec-10]
 DEBUG c.t.c.d.data.OrganizationInfoQuery - organizationService.findOrganizationInfo(null, {value=true}, 0, 50)

Any ideas?

Thanks

public DynamicComboBox(@NotNull SuggestionProvider provider){

    this.provider = provider;
    final SuggestionProvider inProvider = provider;
    setNewItemsAllowed(true);
    setImmediate(true);

    setNewItemHandler(new NewItemHandler() {
        @Override
        public void addNewItem(String newItemCaption) {

            combo.removeAllItems();
            String[] sugg = inProvider.getSuggestions(newItemCaption);
            if (sugg.length > 0){
                
                combo.addItems(inProvider.getSuggestions(newItemCaption));
                combo.setValue(sugg[0]

);

            }

        }
    });

I know this thread is old, but it is the top google result so I thought I’d post a solution.

The Vaadin addon “Viritin” has a component called LazyComboBox that does exactly what you need.

https://vaadin.com/directory#!addon/viritin