Right way to make form aware of changes in underlying data model?

Hi guys,
I am still new to Vaadin. I struggle to make form widgets aware of changes in underlying data model (data source).
I read the Vaadin Book, I searched the forums up and down but all events seems to consider only propagating changes from widget to data model.
But I am interested in the opposite direction.
How to make form widgets reflect of changes in property value?

To demonstrate what I mean I have created a very simple example:

  • simple form
  • one TextField to show random number
  • one button to generate new random number
  • the new value is set to data model

This does not work, new random number is generated, but the new value is not shown in the TextField:


/**
 * Demonstrate how to update form fields when underlying data model (data
 * source) changes. How to make form fields reflect changes in the properties.
 */
public class TestRefreshFormPanel extends Panel {
	private static final long serialVersionUID = 1L;

	private static Random randomGenerator = new Random();

	// this only member variable represents whole data model in this simplified example 
	private int randomNumber = randomGenerator.nextInt();

	private Form form;

	/**
	 * Create simple form with one text field showing random number and a button
	 * triggering next random number generation (data model change) The new
	 * number should show in the form field.
	 */
	public TestRefreshFormPanel() {
		setSizeUndefined();
		form = new Form();
		Layout l = form.getLayout();
		l.addComponent(new TextField("Random number", new RandomFieldProperty()));
		l.addComponent(new GenerateNextRandomNumberButton("Generate random number"));
		addComponent(form);
	}

	public class GenerateNextRandomNumberButton extends Button {
		private static final long serialVersionUID = 1L;

		public GenerateNextRandomNumberButton(String caption) {
			super(caption);
			addListener(new GenerateNextRandomNumberButtonAction());
		}
	}

	/**
	 * Make change to the underlying data model - generate a random number.
	 * ..and make the displaying field somehow show the new value.
	 */
	public class GenerateNextRandomNumberButtonAction
			implements Button.ClickListener {
		private static final long serialVersionUID = 1L;

		public void buttonClick(Button.ClickEvent event) {
			randomNumber = randomGenerator.nextInt();
			System.out.println("New random number was generated: " + randomNumber);
		}
	}

	/**
	 * Simple property implementation mapping directly to data model
	 * (dataSource) field
	 */
	public class RandomFieldProperty implements Property {
		private static final long serialVersionUID = 1L;

		@Override
		public String toString() {
			return (String) getValue(); // does not work unless toString() is implemented too!
		}

		@Override
		public Object getValue() {
			System.out.println("Property value was requested. Current value is " + randomNumber);
			return Integer.toString(randomNumber);
		}

		@Override
		public void setValue(Object newValue) throws ReadOnlyException, ConversionException {
		}

		@Override
		public Class<?> getType() {
			return String.class;
		}

		@Override
		public boolean isReadOnly() {
			return false;
		}

		@Override
		public void setReadOnly(boolean newStatus) {
		}
	}
}

The only way I found was to use requestRepaintAll() just after the new value was assigned with a new random number.


	public class GenerateNextRandomNumberButtonAction
			implements Button.ClickListener {
		private static final long serialVersionUID = 1L;

		public void buttonClick(Button.ClickEvent event) {
			randomNumber = randomGenerator.nextInt();
			System.out.println("New random number was generated: " + randomNumber);
			//form.requestRepaint(); // DOES NOT WORK! no visible change in the field
			//form.requestRepaintRequests(); // DOES NOT WORK! no visible change in the field
			//form.getLayout().requestRepaint(); // DOES NOT WORK! no visible change in the field
			form.getLayout().requestRepaintAll(); // THIS and ONLY this way works. Why!?!? Any better way?
			// also note, there is no form.requestRepaintAll()
		}
	}

This works, it does show the new random number, but I still feel this is not the best way.
Another suspicious thing is requestRepaintAll() is present on layout of the form, not on the form itself.

Any suggestion welcome
Thank you

There is another solution (I found today 8.2.2011)

http://demo.vaadin.com/book-examples/book/#datamodel.items.beanitem.valuechangenotifier
.

The example given is very similar to my with random number, it is just even more simpler, dully setting the same string again and again. That is:
thing.setName(“Modified property”) and thing.setAge(42);

It is marked as experimental study.

It points out important observation, I can confirm too (citation):

Solution is based on:

  • Wrapper (Decorator) around Thing → ExtendThing, suctom decorator with setters firing customized notifier.fireValueChange(“age”);
  • Wrapping the wrapper to BeanItem (VaadinClass) BeanItem thingItem = new BeanItem(thing);
  • Core part causing reflecting chnages is in the notifier listener :
    MethodProperty<?> prop = (MethodProperty<?>)thingItem.getItemProperty(propertyId);
    prop.fireValueChange();

Looks like a lots of boilerpate code :frowning: