ComboBox firing addNewItem even though item exists

Hello,

I have a problem with the ComboBox when accepting new items. I provide an array of strings (or objects) to the ComboBox, and I select an initial value on the ComboBox,
but when I click in the ComboBox (set focus), and then tab or make it lose focus, it fires addNewItem with the item I have programmatically selected.

However if I bring up the suggestion list first and then make it lose focus, this does not happen.

Also, this only happens the first time you focus/defocus the component.

Is there something I have missed in the behavior of this component? (Or is this perhaps a bug?)

Here is a small program that reproduces the problem:


public class MyVaadinApplication extends Application implements NewItemHandler {

    private Window window;

    private String[] strings = new String[]
 { "Test1", "Test2", "Test3", "Test4" };

    @Override
    public void init() {
        window = new Window("My Vaadin Application");
        setMainWindow(window);

        ComboBox comboBox = new ComboBox("Test Combobox", Arrays.asList(strings));
        comboBox.setNewItemsAllowed(true);
        comboBox.setNewItemHandler(this);
        comboBox.setImmediate(true);
        comboBox.select(strings[2]
);
        window.addComponent(comboBox);
    }

    public void addNewItem(String newItemCaption) {
        window.showNotification(newItemCaption);
    }

}

Thanks, Vilhelm

Not positive as I haven’t used ComboBox yet, but I noted you set the values with select() method, which no doubt simulates the user having selected them. In my select boxes, we use setValue() to set the current value. So perhaps your current value is null, then your select() chooses them so it seems like a new value has been chosen.

I have tried using both the select and the setValue method on the ComboBox, but I still experience the same behavior. (Also tried using both :slight_smile: )

Can the problem be related to the fact that the gwt-component doesn’t create the menu items until the user brings up the list of suggestions?

Hi,

This seems to be a bug - I added a ticket:
#7215

Normally this is not noticed, since the container rejects duplicate itemIds, but is still a bad thing™.

Best Regards,
Marc

Nice that you’ve added ticket out of this!

I also have a search field in my app done with combobox which works nicely in most situations. Only problems are:

  1. addNewItem()-method is invoked when I hit enter or my search field loses focus. I would like to add new items only when enter gets hit.
  2. When combobox loses focus, I would like to keep the string typed to it even tough it’s not added as an item to the combobox yet

By the way is there some workaround for the problems mentioned above, before the ticket gets fixed?

still not fixed after half a year past :frowning:
even if container rejects this items, the most usual case to use this handler - to write new item to database, and even if we handle duplicates correctly on DB level - its still several DB calls in place, and thats no good at all

For me the same:


When combobox loses focus, I would like to keep the string typed to it even tough it’s not added as an item to the combobox yet

No solution for this?

Bump!

Still no fix for this? I saw that the ticket associated with this call was last modified 15 months ago!

Can someone please help cause this is a big probem for me!

Many thx!

I believe it hasn’t been addressed because

  • it only affects you when using a container that does not handle duplicate addition and
  • there is an easy workaround: check if already there (it might require some DB queries, but those might get served from the cache as the item was just added) and
  • there are many other bugs to address.

You could also add a check in the ComboBox on the server side to see if this item was just added to avoid the extra database calls in most cases - this should be easy enough with a custom Select.NewItemHandler.

If this is nevertheless a big issue for you, you can prioritize the bug if you have the (commercial)
Pro Account
.

Hi Henri,

Thx for the quick reaction!

The problem is that when the new item handler fires (when selecting an existing object) the values of this object are reset, since I only have the new item caption to init the new item object.

All other properties of the object are gone with the wind, such as the database id of the item. Because of this I cannot evaluate if the object already exists in db or that it is new.

To clarify: I have an editable table in which translations can be provided for a definition. This table has two combobox columns: language and translation. If you select a language the translation combobox is automatically filtered based on the language you chose.

To implement this dependent combobox update behaviour I used the code example as provided by[ b]
Joonas Lehtinen i
[/b]n the following post:
Select dependent on other select in table
. This works very well!

In analogy to this example, here is my custom TableFieldFactory:


public class CustomTableFieldFactory implements TableFieldFactory {

    private IndexedContainer languages;
    private SessionFactory sessionFactory = SessionFactoryHelper.getSessionFactory();
    private Session session;
    private HashMap<String, BeanItemContainer<Definition>> definitions = new HashMap<String, BeanItemContainer<Definition>>();

    public CustomTableFieldFactory() {

        session = sessionFactory.openSession();
        languages = new IndexedContainer();
        BeanItemContainer<Definition> container;
        for (Language lang : LanguageServiceImpl.getInstance().getAllLanguages(session)) {
            languages.addItem(lang);
            container = new BeanItemContainer<Definition>(Definition.class);
            definitions.put(lang.getCode(), container);
            for (Definition def : DefinitionServiceImpl.getInstance().getAllDefinitionsForLanguage(session, lang.getId())) {
                container.addItem(def);
            }
        }

        container = new BeanItemContainer<Definition>(Definition.class);
        definitions.put("ALL", container);
        container.addAll(DefinitionServiceImpl.getInstance().getAllDefinitions(session));

        session.close();
    }

    public Field createField(Container container, Object itemId,
                             Object propertyId, Component uiContext) {
        try {
            session = sessionFactory.openSession();
            if ("Language".equals(propertyId)) {
                SelectLanguages selLanguages = new SelectLanguages(session, true, true);

                selLanguages.setPropertyDataSource(container.getContainerProperty(itemId, propertyId));
                selLanguages.setImmediate(true);
                return selLanguages;
            }
            else {
                final ComboBoxDefinitions cbxDefinitions = new ComboBoxDefinitions(session, false);
                cbxDefinitions.setWidth("330px");
                /* allow new items if no existent translation could be found */
                cbxDefinitions.setNewItemsAllowed(true);
                cbxDefinitions.setNewItemHandler(new NewItemHandler() {

                    @Override
                    public void addNewItem(String newItemCaption) {
                        System.out.println("nieuwe aangemaakt: "+newItemCaption);
                        Definition def = new Definition();
                        def.setDefinition(newItemCaption);
                        def.setStatus(0);
                        cbxDefinitions.addItem(def);
                        cbxDefinitions.setValue(def);
                    }
                });
                
                final Property divisionProperty = container.getContainerProperty(itemId, "Language");
                ((Property.ValueChangeNotifier) divisionProperty).addListener(new ValueChangeListener() {

                    public void valueChange(ValueChangeEvent event) {
                        Language newLanguage = ((Language) divisionProperty.getValue());
                        if (newLanguage != null) {
                            cbxDefinitions.setContainerDataSource(("".equals(divisionProperty.getValue()) || divisionProperty.getValue() == null) ? definitions.get("ALL") : definitions.get(newLanguage.getCode()));
                        }
                        else {
                            cbxDefinitions.setContainerDataSource(null);
                        }
                    }
                });

                cbxDefinitions.setContainerDataSource(("".equals(divisionProperty.getValue()) || divisionProperty.getValue() == null) ? definitions.get("ALL")
                        : definitions.get(((Language) divisionProperty.getValue()).getCode()));

                cbxDefinitions.setPropertyDataSource(container.getContainerProperty(itemId, propertyId));
                cbxDefinitions.setImmediate(true);
                return cbxDefinitions;
            }
        }
        finally {
            session.close();
        }

    }

As you can see, if a new item is added I create a new Definition object, set the caption on it, set the status to 0 and add it to the combobox. This is exactly what I want for
new
items being added!

But if I select an
existing
item and the newitemhandler fires as described above this means that the existing (fully filled) object is replaced with a new one which has only 2 properties set. The database id of the item is gone, hence I cannot distinguish between new and existing items.

I know that the way around this is to check in addNewItem method if the item already exists in db. If exitsts no logic is performed, else new object is inited and added to collection.

In this case I need to search the db tables based on caption text string which is not good for performance since the table has a large number of rows and the caption can be fairly large.

Could you please point out if there any alternative solutions? I’m quite new to Vaadin so there might be a very simple alternative I am missing :slight_smile:

Anyways,

Thx for taking the time to read my post!

regards,

Kim

The Netherlands