Selectbox with a model, model property change should update selected value.

First I try to explain what I need. I want to have a TextField for entering a personid. Then on the right side of the text field should be a select box where a user can choose a person by name if he doesn’t know the personid.

If the user knows the personid, he will enter the personid and the selectbox should switch to the corresponding name. I know how I would do this with SWT but I couldn’t find out how to do it with vaadin.

In SWT I would have a model class with a property personid and property person. Then the text field is bound to the property personid in the model. If someone enters a personid it would call the setPersonid and there I would load the person with this personid and set the person property with the loaded person. Afterwards I would send a fire property change person. Because the selected value from the select box is bound to this person it would change automatically.

In vaadin I found out that I can bind the personid like this:

MyModel model = new MyModel();
BeanItem<MyModel> beanItem = new BeanItem<MyModel>(model);
Property personidProperty = beanItem.getItemProperty("personid");
TextField personidText = new TextField("Personid", personidProperty);

This would set the personid in the model object if someone enters a personid. But I don’t know how i can bind the person within the model with the selected value from the select box. And also I don’t know how to fire a property change.

Maybe in vaadin the architecture is different and the way to do it is another one. If someone could give me an example how to reach this I would appreciate.
Please share if you have any idea.
Thank you
chris

First of all, I wouldn’t use two separate components to do this but use a
ComboBox
displaying the personid and the name of the person as one string.

If you want to do it with two separate components you need to add a
Property.ValueChangeListener
to your TextField and set it
immediate
. This way ValueChangeEvents are fired right away. In your ValueChangeListener you would get the value of your TextField, identify the correct item in your SelectBox and
select(Object)
it.

Hi Tobias thank you for your reply. It’s a user request to have two components. If I understand you correctly the TextField would change the selectbox directly without going through a model. Means a UI-Component updates a UI-Component. Somehow this sounds not very nice to me. Ok in this simple case I could do it. But what if the change in the TextField has more than one component which have to listen? For example an additional person tree where the right one should be selected too.

As far as I know a UI-Component should listen to a model property like this.

  1. Register the UI-Component as a listener from a model property
  2. Model fires a property change event.
  3. UI-Component handles the change event.
  4. Possible to register more than one component to listen to this change.

I really don’t like the idea that the ui-component updates another ui-component, somehow this doesn’t seem to be MVC. Maybe I’m wrong then please let me know…

br
chris

I understand your point, but the Field-interface extends the Property-interface so from a certain angle you are actually sending events from a Model to a Model :wink: … Well, of course you are correct, the Field and it’s selected Value aren’t properly separated here, but that’s the way it is currently. Every field holds it’s selected / inputted value in a Property that is actually the Field itself.

The type of a TextField is a String, so you need something in between to map between the string and a PersonId. The easiest option might be to use FieldWrapper from the add-on
CustomField
and its conversion support.

Make sure the TextField is in immediate mode so the value gets updated when the field loses focus or e.g. Enter is pressed.

Then just set the same property (personIdProperty) as the data model for both the TextField (or rather its converter) and the select box, the underlying property will automatically fire events and update both.

Hello Henri

Thank you for your help. I’ll try it out.
If you have a simple example, please post.
THX

Today I tried to use the FieldWrapper without success for several hours. Could you give an example for this use case, please? I run out of time :frowning:

There are some examples in the
CustomField project
. Most of them are somewhat more complicated than what I think you are trying to achieve, but see e.g. ConversionExample (does not use a separate converter but directly overrides methods in CustomField) and the use of a field factory e.g. in NestedPersonForm.

Then use the same data source for the FieldWrapper around your TextField and the select. Make sure the text field is in immediate mode.

Hi all, just to let you know I could solve it. Here my source:

public AttendanceForm(SpringContextHelper springContextHelper) {
		
this.springContextHelper = springContextHelper;
this.personService = (PersonService)springContextHelper.getBean(PersonService.class);
		
setCaption("Anwesenheit erfassen");
		
ObjectProperty<Person> personProperty = new ObjectProperty<Person>(new Person(), Person.class);
		
TextField textField = new TextField("Member ID", personProperty);
PersonIdField personIdField = new PersonIdField(personService, textField, Person.class);
		
personIdField.setPropertyDataSource(personProperty);
personIdField.setImmediate(true);
		
this.layout = new HorizontalLayout();
layout.addComponent(personIdField);
		
BeanItemContainer<Person> container = new BeanItemContainer<Person>(Person.class);
container.addAll(personService.getPersonsByTypeId(2));
		
final Select select = new Select("Kursteilnehmer", container);
select.setItemCaptionMode(Select.ITEM_CAPTION_MODE_PROPERTY);
select.setItemCaptionPropertyId("fullName");
select.setPropertyDataSource(personProperty);
select.setImmediate(true);
	    
layout.addComponent(select);
getLayout().addComponent(layout);
	    	
}
public class PersonIdField extends FieldWrapper<Person>{
	
	private static final long serialVersionUID = 3111237660794697310L;
	
	private PersonService personService;

	public PersonIdField(PersonService personService, TextField wrappedField, Class<Person> propertyType) {
		super(wrappedField, propertyType);
		this.personService = personService;
		VerticalLayout layout = new VerticalLayout();
        layout.setMargin(false);
        layout.addComponent(wrappedField);
        setCompositionRoot(layout);
	}

	@Override
	protected Person parse(Object formattedValue) throws ConversionException {
		long personid =Long.parseLong((String)formattedValue);
		Person person = personService.getOnePerson(personid);
		return person;
	}
	
	@Override
	protected Object format(Person value) {
		if (value == null || value.getPersonid() == null) {
			return "";
		}
		return value.getPersonid().toString();
	}
}

Important to mention implement equals in person properly otherwise the person will not be seleted after you enter a personId. That was my fault.
Thank you to everybody for the support.