Important Notice - Forums is archived
To simplify things and help our users to be more productive, we have archived the current forum and focus our efforts on helping developers on Stack Overflow. You can post new questions on Stack Overflow or join our Discord channel.

Vaadin lets you build secure, UX-first PWAs entirely in Java.
Free ebook & tutorial.
Dynamic form
Hi
I have a form with a FormFieldFactory and two Selects. One is productFamilyId and the other is productId. The productId item list depends on the productFamilyId value. At field creation time there is no problem because the form item, i.e. all the properties, is available
public Field createField(Item item, Object propertyId, Component uiContext) {
What I don't know is how to get the other field and force it to refill options.
I have attached a ValueChangeListener to the productFamilyId and I know the new value when it changes.
f6.addListener( new ValueChangeListener() {
@Override
public void valueChange(ValueChangeEvent event) {
Integer newProductFamilyId = (Integer) event.getProperty().getValue();
event.getProperty().getType();
}
});
I added a ValueChangeListener to form but id doesn't receive any event. Is there any way to receive an event with the current item when a property changes. I've tried to add a Property.ValueChangeListener to the form, but the IDE says that interface is not implemented.
How to get access to the productId form field? Is there any other way to implement cross-field events?
Any ideas?
Thanks
Well, I've found a way to do it: create a class that implements ValueChangeListener with access to all the form fields. So, when a Select field changes, I can fill another select.
I have implemented an interface to receive all the ValueChangeEvents of the form
@Override
public void crossFieldChange(Form fc, ValueChangeEvent event) {
Property p = event.getProperty();
try {
Field f = (Field) p;
if (f.getCaption().equals(f6.getCaption())) {
AbstractSelect as = (Select) f7.getField();
as.removeAllItems();
List<E1> lc = service.findOptions((Integer) p.getValue());
for (E1 c: lc) {
as.addItem(c.getUserId());
as.setItemCaption(c.getUserId(), "" + c.getUserId() + " " + c.getUserName());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
Now when f6 select value changes, f7 select list is updated. Both fields are required. When f6 changes a "!" appears near f7 because f7 becomes blank: current f7 value does not match any option of the new list. I select a new option in f7 and the "!" disappears but then commit fails. Exception is Validator$EmptyValueException. I've checked the form doesn't like removing and adding items.
Every form field is immediate, form is also immediate, not invalidCommited and not writeThrough.
Any ideas?
Thanks
Well
The cause is explained here http://vaadin.com/forum/-/message_boards/message/128762#_19_message_91549
What it is not still clear is if valueChange() call during form.commit() is a bug or a feature.
Regards
Here's one way to do it, if I understood your intention correctly:
public class MoonBean implements java.io.Serializable {
String planetName;
String moonName;
MoonBean() {
planetName = "";
moonName = "";
}
public void setPlanet(String planet) {
this.planetName = planet;
}
public String getPlanet() {
return planetName;
}
public void setMoon(String moon) {
this.moonName = moon;
}
public String getMoon() {
return moonName;
}
}
void selectExample() {
VerticalLayout layout = new VerticalLayout();
final String[][] planets = new String[][] {
{"Mercury"},
{"Venus"},
{"Earth", "The Moon"},
{"Mars", "Phobos", "Deimos"},
{"Jupiter", "Io", "Europa", "Ganymedes", "Callisto"},
{"Saturn", "Titan", "Tethys", "Dione", "Rhea", "Iapetus"},
{"Uranus", "Miranda", "Ariel", "Umbriel", "Titania", "Oberon"},
{"Neptune", "Triton", "Proteus", "Nereid", "Larissa"}};
class MyFieldFactory implements FormFieldFactory {
public Field createField(Item item, Object propertyId,
Component uiContext) {
final Form form = (Form) uiContext;
String pid = (String) propertyId;
if (pid.equals("planet")) {
final ComboBox planet = new ComboBox("Planet");
planet.setNullSelectionAllowed(false);
planet.setInputPrompt("-- Select a Planet --");
for (int pl=0; pl<planets.length; pl++)
planet.addItem(planets[pl][0]);
planet.addListener(new Property.ValueChangeListener() {
public void valueChange(ValueChangeEvent event) {
String selected = (String) planet.getValue();
if (selected == null)
return;
ComboBox moon = (ComboBox) form.getField("moon");
for (int pl=0; pl<planets.length; pl++)
if (selected.equals(planets[pl][0])) {
moon.removeAllItems();
moon.setInputPrompt("-- Select a Moon --");
moon.setNullSelectionAllowed(false);
for (int mn=1; mn<planets[pl].length; mn++)
moon.addItem(planets[pl][mn]);
moon.setEnabled(planets[pl].length > 1);
}
}
});
planet.setImmediate(true);
return planet;
}
if (pid.equals("moon")) {
ComboBox moonSel = new ComboBox("Moon");
moonSel.setEnabled(false); // Select a planet first
return moonSel;
}
return null;
}
}
// The form
Form myform = new Form();
myform.setCaption("My Little Form");
myform.setFormFieldFactory(new MyFieldFactory());
// Create a bean to use as a data source for the form
MoonBean moonbean = new MoonBean();
myform.setItemDataSource(new BeanItem<MoonBean>(moonbean));
myform.setVisibleItemProperties(new Object[] {"planet", "moon"});
layout.addComponent(myform);
setCompositionRoot(layout);
}
See the online example
Oops, remove this from the example:
MoonBean() {
planetName = "";
moonName = "";
}
or otherwise the input prompt doesn't work correctly.
Marko,
I'm also interested in dynamic forms and I'm trying to understand this topic with help of your example.
Selection and dynamic moon refilling works fine, but I have a problem with form.commit(): when I use commit() the moon ComboBox is emptied.
Details:
I added these lines of code to your example:
Button apply = new Button("commit()", new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
try {myform.commit();}
catch (Exception e) {System.out.println(e.toString());}
}
});
addComponent(apply);
After starting the application I first choose "Jupiter" and then "Callisto" as moon value. Fine! Then I press the commit button. What happens is that the first ComboBox still displays "Jupiter", but the second ComboBox displays "-- Select a Moon --" again.
Is there a good way to make sure, that the dynamic filled ComboBox still shows the selected value after commit()?
Thanks, Thorsten
Hi Vaadin team,
my congratulations on the 6.3 release! It shows great promise for the future.
I hoped that this special dynamic form effect would suddenly disappearing with the new release but sadly it was not.
So I would like to repeat my question...
Thanks a lot, Thorsten
Hi,
I just want to bring up this topic because the effect I mentioned still exists in 6.4. It is difficult to create dynamic forms because of this. I hope there will be a solution/workaround in future.
Thanks,
Thorsten
Thorsten A: Hi,
I just want to bring up this topic because the effect I mentioned still exists in 6.4. It is difficult to create dynamic forms because of this. I hope there will be a solution/workaround in future.
Thanks,
Thorsten
Isn't it a matter of storing the "moon" combobox value before clearing it and resetting it afterwards?
Something like:
if (selected.equals(planets[pl][0])) {
-> Object moonValue = moon.getValue();
moon.removeAllItems();
moon.setInputPrompt("-- Select a Moon --");
moon.setNullSelectionAllowed(false);
for (int mn = 1; mn < planets[pl].length; mn++) {
moon.addItem(planets[pl][mn]);
}
moon.setEnabled(planets[pl].length > 1);
-> moon.setValue(moonValue);
}
I'm also having the same problem under 6.7.0. Under form.commit, the dependant select goes empty, and the commit doesn't succeed. Also, it should be pointed out that when I want to edit the same bean again, it will execute:
formsetItemDataSource(bi)
Again, but this second time, an exception is thrown:
Cause: java.lang.NullPointerException
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:532)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:164)
at com.vaadin.ui.AbstractComponent.fireEvent(AbstractComponent.java:1219)
at com.vaadin.ui.AbstractField.fireValueChange(AbstractField.java:906)
at com.vaadin.ui.AbstractField.setPropertyDataSource(AbstractField.java:645)
at com.vaadin.ui.Form.setItemDataSource(Form.java:770)
at com.vaadin.ui.Form.setItemDataSource(Form.java:718)
I'm running out of ideas or alternatives.. any help appreciated.