I created a grid and added a header row to it. In the header row i added textfield for each column (to enable filtering of that column based on the text entered). Columns could be of Non String type (Numeric, Integer, Double, etc). I want that validation error is shown when a user enters a string inside the text field where the column type is non string.
I get InvalidValueException in the console, but nothign on the UI.
for(Grid.Column col : grid.getColumns())
{
com.vaadin.ui.Field field = beanFieldGroup.buildAndBind(null,col.getPropertyId());
if(TextField.class.isAssignableFrom(field.getClass()))
{
((TextField)field).setNullRepresentation("");
((TextField)field).setTextChangeEventMode(AbstractTextField.TextChangeEventMode.LAZY);
((TextField)field).setImmediate(true);
((TextField)field).setValidationVisible(true);
((TextField)field).addTextChangeListener(new FieldEvents.TextChangeListener() {
@Override
public void textChange(FieldEvents.TextChangeEvent event) {
try {
((TextField)field).setValue(event.getText());
// workaround cursor position problem
((TextField)field).setCursorPosition(event.getCursorPosition());
((TextField)field).validate();
} catch (InvalidValueException e) {
((TextField)field).setComponentError(new SystemError(e));
}
}
});
}
filteringHeader.getCell(col.getPropertyId()).setComponent(field);
}
If I am not wrong, I shouldn’t even be writing the text listner. Since I have BeanFieldGroup, it should automatically create Validators for the text fields and trigger it.
Thanks for your reply. Unfortuntely it leads to same result
The InvalidValueException you throw leads to stacktrace printing on the console
SEVERE:
com.vaadin.data.Validator$InvalidValueException: Could not convert value to Integer
at com.vaadin.ui.AbstractField.validate(AbstractField.java:985)
at com.vaadin.ui.AbstractField.validate(AbstractField.java:960)
at com.aahar.ui.design.grid.GridPanel$2.textChange(GridPanel.java:266)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
However I want to show the User on the UI that the value entered was not a number
Could it be because the fields are added to the cols of header row of a grid? Is it possible that Header row is not suppose to show validation errors??
I guess i did remove the TextChangeListner, I have a break point inside the validator, Now the break point is not even hit when i loose focus after entering a wrong value in the textfield. Below is my code
[code]
T instance = clazz.getConstructor().newInstance();
final BeanFieldGroup beanFieldGroup = new BeanFieldGroup(clazz);
beanFieldGroup.setItemDataSource(instance);
beanFieldGroup.setFieldFactory( new FieldGroupFieldFactory() {
@Override
public <T extends com.vaadin.ui.Field> T createField(Class<?> dataType, Class<T> fieldType) {
T field = DefaultFieldGroupFieldFactory.get().createField(dataType, fieldType);
if (Number.class.isAssignableFrom(dataType)) {
field.addValidator(value -> {
if (value != null && !value.toString().isEmpty() && !value.toString().matches("-?\\d+(\\.\\d+)?")) {
throw new InvalidValueException("Please specify a number");
}
});
}
return field;
}
});
for(Grid.Column col : grid.getColumns())
{
com.vaadin.ui.Field field = beanFieldGroup.buildAndBind(null,col.getPropertyId());
if(TextField.class.isAssignableFrom(field.getClass()))
{
((TextField)field).setNullRepresentation("");
((TextField)field).setTextChangeEventMode(AbstractTextField.TextChangeEventMode.LAZY);
((TextField)field).setImmediate(true);
((TextField)field).setValidationVisible(true);
}
filteringHeader.getCell(col.getPropertyId()).setComponent(field);
}
[/code]I could not create an instance of new DefaultFieldGroupFieldFactory() like you did. Probably different Vaadin versions, but I suppose what I did should have worked as well.
VerticalLayout lay = new VerticalLayout();
lay.setSizeFull();
Grid grid = new Grid("Testing");
BeanItemContainer<RecipeList> cont = new BeanItemContainer<>(RecipeList.class);
cont.addAll(stb.getRecipeList());
grid.setContainerDataSource(cont);
grid.setSizeFull();
BeanFieldGroup<RecipeList> beanFieldGroup = new BeanFieldGroup<>(RecipeList.class);
beanFieldGroup.setFieldFactory(new DefaultFieldGroupFieldFactory() {
@Override
public <T extends Field> T createField(Class<?> dataType, Class<T> fieldType) {
T field = super.createField(dataType, fieldType);
if (Number.class.isAssignableFrom(dataType)) {
field.addValidator(value -> {
if (value != null && !value.toString().isEmpty() && !value.toString().matches("-?\\d+(\\.\\d+)?")) {
throw new InvalidValueException("Please specify a number");
}
});
}
return field;
}
});
HeaderRow filteringHeader = grid.addHeaderRowAt(0);
for (Grid.Column col : grid.getColumns()) {
Field<?> field = beanFieldGroup.buildAndBind(null, col.getPropertyId());
filteringHeader.getCell(col.getPropertyId()).setComponent(field);
}
lay.addComponent(grid);
testingTab.setContent(lay);
Everything as you told me to do. I still do not see any error on the UI. The debugger does enter the InvalidValueException, I follwed the code a bit and it seems the code flow does want to communicate the error to the GUI. BUT it doesnt.
Please see attched the GUI screenshot. (FOODID textfield is bound to Integer type)
I would expect the text field with wrong entry to be marked red or something,
It is frustrating to learn you can make it work and I cannot
You are using the “reindeer” theme. Unless, the “valo” theme, I think “reindeer” won’t change the look of the field when a validation fails, but you should see a tooltip with the error when the validation fails if you put the mouse pointer over the field.
Hi there,
using vaadin 8.0.5 and the Grid row Editor in buffered mode,
bean is not updated when Validator fails, but Editor closes and I see no Error message …
I expect it to stay open and tell me why validation fails.
final TextField nameEditor = new TextField();
final Binder<Site> binder = grid.getEditor().getBinder();
final Binder.Binding<Site, String> nameBinding = binder
.withValidator( site -> {
System.err.println("VALIDATE name");
return site.getName().length() < 20;·
}, "Name must be less than 20 characters long")
.bind(nameEditor, Site::getName, Site::setName);
grid.addColumn(Site::getName)
.setId("name")
.setCaption("Site Name")
.setEditorBinding(nameBinding);
grid.getEditor().setEnabled(true);
//grid.getEditor().setErrorGenerator((colMap, status) -> {
// System.err.println(" Editor has errors : " + status.hasErrors());
// return "Error String";
//});
grid.getEditor().getBinder().addStatusChangeListener( evt ->
System.err.println("Status Change :" + evt. ▸ hasValidationErrors()));
grid.getEditor().addSaveListener( evt -> {
System.err.println("SAVE");
try { evt.getBean().save(); }
catch(Exception e) { System.err.println(e.getMessage()); }
});
[java]
VALIDATE name
[java]
Status Change :true
thanks in advance
Hi, thanks for your attention, I’ve tested it and nailed the difference,
using forField(…) is the magic incantation, but then your validator deals with the value of the field
// works
final Binder.Binding<Site, String> binding =
binder.forField(name)
.withValidator(string -> string.length() > 6, "at least 6 chars")
.bind(Site::getName, Site::setName);
as I would prefer to work with my object and simply use one of it’s method, the following fails
// does not work
final Binder.Binding<Site, String> binding =
binder
.withValidator(obj -> obj.isValid(), "is not valid")
.bind(name, Site::getName, Site::setName);
using setEditorComponent(…) or not doesn’t change anything,
validation is not called at value change.