Select with non-String objects + Property.ValueChangeEvent

Hi,

today I’m hurting my brain with Selects. All the samples seemed to be trivial using only Strings as the data source. I’d like to add “real” objects as items, so that the change event listener gets the full object and not only its name.

The objects fit in nicely, but I can’t get the right property to show as the caption. What the f am I doing wrong?


Property.ValueChangeListener listChangeListener; // set from outside
Select sf = new Select("Please select one");
sf.setItemCaptionPropertyId("value");
// KeyValuePair is basically a Map.Entry with properties key and value
for(KeyValuePair dto:kayttajatyypit) {
  sf.addItem(new BeanItem(dto));
}
sf.addListener(listChangelistener);

The other problem is that when I click the first selection (by default the null value is selected), I get no change event. The event occurs right after I click the dropdown open again, and then it happens 3 times in a row!

try adding sf.setImmediate(true);

Damn, a colleague said he tested it with no luck. Always should do everything myself :wink:

Immediate helped and the event happens right away. But it still happens many times per a selection.

Using version 5.3.1 (same problem with 5.3.0), and FFox 3.0.8… Same problem with IE7.

Triple value change events sounds like you are somehow managing to add the same listener three times to the select.

The problem with adding items is that addItem() does not work they way you have assumed. The Item addItem(Object itemId) method takes an itemId and creates an Item associated with that id. You are creating the Item yourself and passing it as an itemId. Creating an Item is the container’s responsibility and the default container for a Select is an IndexedContainer so the result will not be at all what you expect.

There is an experimental BeanItemContainer included in 5.3.1 and a more complete version will be included in the upcoming 5.4.0. Using this you can accomplish what you want:


        Select sf = new Select("Please select one");
        sf.setItemCaptionPropertyId("value");
        sf.setImmediate(true);
        BeanItemContainer<Data> container = new BeanItemContainer<Data>(Data.class);
        sf.setContainerDataSource(container);

        for (Data d : dataObjects) {
            container.addItem(d);
        }

You can also use POJOs directly as itemId’s.

Example:


for (User u : dao.getAllUsers()) {
  select.addItem(u);
  //Set caption if you do not want to use User.toString();
  // select.setItemCaption(u, u.getFirstName() + " " + u.getLastName());
}

That seems like what I’ve tried to accomplish. Now how do I get the itemId out of the change event?

Artur: You’re partially right. I have the same listener in 3 different Selects with same backing bean wrapped by BeanItem. I guess that when I change one select, the BeanItem changes the value to the bean, and that somehow triggers another change event to the other selects. Weird…

ItemId is the current value:

public void valueChange(ValueChangeEvent event) {
  User u = (User) event.getProperty().getValue();
}

Can’t agree, I’m getting a ClassCastException because the getValue returns a String… :frowning:

field.getValue() returns the itemID, so if you’ve put the user object as id you should get users too.

I’d suggest you’d specify a little closer the valueChange method like this:


public void valueChange(ValueChangeEvent event) {
  if(event.getProperty() == sf) {
   User u = (User)sf.getValue();
  } 
  // possility to now do else if -clauses if you 
  // have the listener attached to multiple fields
}

Just make sure you add the items with sf.addItem(user); (or directly to the container in the same manner.)

Oh this is turning to comedy :slight_smile:

Now I’m getting only one hit at the FieldFactory (changed the order of setFieldFactory and setItemDataSource).

But still I’m getting three events.


    public void valueChange(Property.ValueChangeEvent event) {
    Select valintalista = (Select)((Field.ValueChangeEvent)event).getSource();
        if(valintalista.getCaption().equals("mySelect") {
            Object value = valintalista.getValue();
            log.debug("value is of type " + value.getClass());
        }
    }

First event has my KeyValuePair, but the 2 latter ones have it’s reference as a String… Funny.

Just thinking out loud…

This Select is part of a Form that has a BeanItem as a datasource. Could these duplicate events somehow be caused by that?

And here my monologue continues :slight_smile:

All this s**t was caused by the Form being setWriteThrough(true). Now I only need to handle the state of the Form and ignore these events when I call Form-commit.

Thanks for all the help so far!

One more for the road…

Now that my event handlers are working nicely (checking the item type and doing some casts), the problem has shifted to form’s commit phase. There it uses the item reference as a String although I’d like to the key of my item. Is there a way to interfere, or should I override something in the Select component?

Update:

Things are working quite nicely now that I changed the type of my DTO’s property to match the Select’s item’s type.

So basically all problems solved, thanks for all the tips!