Validation icon not shown in Table fields - vaadin

When I enter edit mode of my Table, I want the data validation exclamation mark icon (!) to be shown as soon as the user goes out of bounds of any of the validation constraints.
First, a couple of notes:
I'm using Vaadin 7, so the Bean Validation addon sadly won't work.
The data validation works as intended.
Now, I have a perfectly working table for which I am using a BeanItemContainer to keep my Person beans inside.
The code for the table and the TableFieldFactory looks something like this:
table.setContainerDataSource(buildContainer());
table.setTableFieldFactory(new TableFieldFactory() {
#Override
public Field createField(Container container, Object itemId, Object propertyId, Component uiContext) {
TextField field = (TextField) DefaultFieldFactory.get().createField(container, itemId, propertyId,
uiContext);
field.setImmediate(true);
if (propertyId.equals("firstName")) {
field.addValidator(new BeanValidator(Person.class, "firstName"));
}
return field;
}
});
The Person bean looks as follows:
public class Person {
#Size(min = 5, max = 50)
private String firstName;
... setters + getters...
}
The problem is that when I type something in the firstName field and then press enter or blur/unfocus that field, no indication whatsoever of error is shown. I have to mouseover the field to see that something is wrong.
My question is two folded...
How do I get the exclamation mark icon to appear when the field is
invalid? (This works for a normal TextField that is not in a Table)
Is there a way to get an immediate response from the invalid field
(show the icon) (i.e. immediately after you type under 5 chars,
without having to press enter or blur/unfocus the field in
question).
Would be great if I could have both questions answered! =)
Thanks in advance!

The Caption, Required Indicator (the red asterisk) and - most importantly here - Error Indicator (exclamation mark) are actually provided by the layouts containing the component, not the component themselves. When editable components are displayed in a table, they are displayed without a layout - that's why no error indicator is displayed.
If I were trying to square this circle, I would look at creating a CustomField as a wrapper for the editable field - and within that CustomField display an error indicator when the wrapped/delegate field becomes invalid. I've not tried this - I've not used editable fields in a table at all - but should be fairly easy to do.
Add a TextChangeListener to the field in FieldFactory, and call field.validate() in the listener. Note, though, that field.getValue() value is not normally changed until blur/unfocus, ergo the validator will be validating the old value - unless you do field.setValue(event.getText()) in the listener. See this post on the Vaadin forum for more details.
This is the sort of thing I meant for a validating wrapper - not tried using it. You'll see initComponent simply returns the field inside a FormLayout, which should give you the icon(s) you're seeking. (You may need to delegate more methods from ValidatingWrapper to delegate than I have- but quick look suggests this may be enough.)
You'd then wrap the field in your tableFieldFactory (second code block)
public class ValidatingWrapper<T> extends CustomField<T> {
private static final long serialVersionUID = 9208404294767862319L;
protected Field<T> delegate;
public ValidatingWrapper(final Field<T> delegate) {
this.delegate = delegate;
if (delegate instanceof TextField) {
final TextField textField = (TextField) delegate;
textField.setTextChangeEventMode(AbstractTextField.TextChangeEventMode.TIMEOUT);
textField.setTextChangeTimeout(200);
textField.addTextChangeListener(new FieldEvents.TextChangeListener() {
#Override
public void textChange(FieldEvents.TextChangeEvent event) {
textField.setValue(event.getText());
textField.validate();
}
});
}
}
#Override
public Class<? extends T> getType() {
return delegate.getType();
}
#Override
protected Component initContent() {
return new FormLayout(delegate);
}
#Override
public Property getPropertyDataSource() {
return delegate.getPropertyDataSource();
}
#Override
public void setPropertyDataSource(Property newDataSource) {
delegate.setPropertyDataSource(newDataSource);
}
}
table.setContainerDataSource(buildContainer());
table.setTableFieldFactory(new TableFieldFactory() {
#Override
public Field createField(Container container, Object itemId, Object propertyId, Component uiContext) {
TextField field = (TextField) DefaultFieldFactory.get().createField(container, itemId, propertyId,
uiContext);
field.setImmediate(true);
if (propertyId.equals("firstName")) {
field.addValidator(new BeanValidator(Person.class, "firstName"));
}
return ValidatingWrapper(field);
}
});

Related

Vaadin data Binder - ComboBox issues

Later Edit: I noticed that by returning one of the options in ValueProvider's apply method leads to having the check mark present, but appears to show the previous select too. I.e. if the current and previous values are distinct, two check marks are shown.
I am having troubles with ComboBox binding. I cannot get the com.vaadin.flow.data.binder.Binder properly select an option inside the combobox - i.e. tick the check mark in the dropdown.
My binder is a "generic", i.e. I am using it along with a Map, and I provide dynamic getters/setters for various map keys. So, consider Binder<Map>, while one of the properites inside the Map should be holding a Person's id.
ComboBox<Person> combobox = new ComboBox<>("Person");
List<Person> options = fetchPersons();
combobox.setItems(options);
combobox.setItemLabelGenerator(new ItemLabelGenerator<Person>() {
#Override
public String apply(final Person p) {
return p.getName();
}
});
binder.bind(combobox, new ValueProvider<Map, Person>() {
#Override
public Person apply(final Map p) {
return new Person((Long)p.get("id"), (String)p.get("name"));
}
}, new Setter<Map, Person>() {
#Override
public void accept(final Map bean, final Person p) {
bean.put("name", p.getName());
}
});
Wondering what could I possibly do wrong...
Later edit: Adding a screenshot for the Status ComboBox which has a String for caption and Integer for value.
Your problem is that you are creating a new instance in your binding, which is not working. You probably have some other bean, (I say here Bean) where Person is a property. So you want to use Binder of type Bean, to bind ComboBox to the property, which is a Person. And then populate your form with the Bean by using e.g. binder.readBean(bean). Btw. using Java 8 syntax makes your code much less verbose.
Bean bean = fetchBean();
Binder<Bean> binder = new Binder();
ComboBox<Person> combobox = new ComboBox<>("Person");
List<Person> options = fetchPersons();
combobox.setItems(options);
combobox.setItemLabelGenerator(Person::getName);
binder.forField(combobox).bind(Bean::getPerson, Bean::setPerson);
binder.readBean(bean);

Unkown Key in Vaadin 14 Grid during selection

I'm using a Grid in Vaadin 14. The grid is in multi-selection mode.
The selection handler takes a couple of seconds to complete and I'm calling setItems(...) at the end to update the items in the grid.
When the user selects another row while the previous selection handler is still running, I get an "Unknown key" error similar to the one described in https://github.com/vaadin/vaadin-grid-flow/issues/322, even though the new set of items still contains the selected item (another object instance but same according to equals()). This seems to be because the keys in the KeyMapper have already been changed due to setItems(), so the key coming from the client is not present anymore.
Is there a way to work around this, for example by disabling selection while the previous request is in progress?
UPDATE
To work around this Vaadin bug, I'm also calling setPageSize() with the exact number of items as argument. But it seems the same problem occurs even if I don't call setPageSize(), so it's probably due to setItems().
Do not change the grids items inside a SelectionListener.
You can still do all the things you wanted, but setting the items anew is not actually needed. In fact it will only create problems as you are experiencing now.
While working at this answer, I realized you will need to do your own Checkbox Column in order to be able to do actions for the one item that was just "selected", instead of removing all then add all selected ones (because much better performance). Here is how that could look.
// in my code samples, a `Foo` item can have many `Bar` items. The grid is of type Bar.
Grid.Column customSelectionColumn = grid.addComponentColumn(item -> {
Checkbox isSelected = new Checkbox();
isSelected.setValue(someParentFoo.getBars().contains(item));
isSelected.addValueChangeListener(event -> {
boolean newSelectedValue = event.getValue();
if(newSelectedValue){
someParentFoo.getBars().add(item)
} else {
someParentFoo.getBars().remove(item);
}
fooRepository.save(someParentFoo);
});
});
// make a Checkbox that selects all in the header
Checkbox toggleSelectAll = new Checkbox();
toggleSelectAll.addValueChangeListener(event -> {
if(event.getValue()){
someParentFoo.getBars().addAll(allGridItems);
} else {
someParentFoo.getBars().removeAll(allGridItems);
}
fooRepository.save(someParentFoo);
grid.getDataProvider().refreshAll(); // updates custom checkbox value of each item
});
gridHeaderRow.getCell(customSelectionColumn).setComponent(toggleSelectAll);
I solved this problem. Vaadin use data as key in HashMap. You need calc hashCode use immutable data fields. For example
public class TestData {
private int id;
private String name;
public TestData(int id) {
this.id = id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

unable to Validate Custom components in SmartGWT

I am unable to get my custom compoenent to be validated in the dynamic form. I tried many versions but it is not working as expected. For e.g. either the label is not showing in BOLD to indicate the field is mandatory and it aloows to save the form without entering anything in the field. Only when the user enters something in the field and deletes it, then the red icon is displayed to the user that the field is mandatory.I dont know what i am missing. please help. code is below
telnumber = new CustomTelephoneTextItem();
telnumber.setName("tel");
telnumber.setTitle("Tel");
telnumber.setTitle(nerpweb.clientFactory.getMessages().tel());
Below is my Custom TextItem which i am using in the above class
public class CustomTelephoneTextItem extends CanvasItem
{
textField_value = new CustomIntegerItem();
textField_value.setShowTitle(false);
textField_value.setWidth(100);
textField_value.setRequired(true);
form.setItems(textField_value, textField_code);
form.validate();
setWrapTitle(false);
this.setCanvas(form);
First, if you want to item title showin bold, you must call item's setRequired(true).
in your code is telnumber.setRequired(true);
Second, if you want to validate item on form.validate(), you must override validate() function in your item and write validation code in this function.
in your code is call form.validate() in CustomTelephoneTextItem validate() function
Here is the code to be implemented to validate custom component
This code will go in your Custom component which you will implement
#Override
public Object getValue()
{
if (validate() && textField_value.getValue() != null)
return textField_value.getValue();
return null;
}
#Override
public void setRequired(Boolean required) {
super.setRequired(true);
}
#Override
public Boolean validate() {
return super.validate();
}
#Override
public void setValidators(Validator... validators) {
textField_value.setValidators(validators);
}
Then in the class where you will create the custom component you will call the setRequired() method,like so
telnumber.setRequired(true);

Vaadin Bean validation for table

When I enter edit mode of my Table, I want to have data validation on all fields in that Table.
First, a couple of notes:
I'm using Vaadin 7, so the Bean Validation addon sadly won't work.
I know the implementation of JSR-303 works, because I tried adding a BeanValidator to a TextField without issues.
Now, I have a perfectly working table for which I am using a BeanItemContainer to keep my Person beans inside.
The Person bean looks as follows:
public class Person {
#Size(min = 5, max = 50)
private String firstName;
#Size(min = 5, max = 50)
private String lastName;
#Min(0)
#Max(2000)
private int description;
... getters + setters...
}
Person beans are added to the BeanItemContainer, which in turn is set to the container data source with setContainerDataSource()
The BeanValidator was added to the table like so:
table.addValidator(new BeanValidator(Person.class, "firstName"));
When I run the application, I have two problems:
When I run the application, the table shows up as intended. However, when I edit the fields and set one of the firstName fields to, say, "abc" - no validation error is shown and the value is accepted
How am I supposed to get BeanValidator to work on all of my tables fields?
When I use table.setSelectable(true) or table.setMultiSelect(true), I get this error:
com.vaadin.server.ServiceException:
java.lang.IllegalArgumentException: [] is not a valid value for
property firstName of type class
com.some.path.vaadinpoc.sampleapp.web.Person
How am I supposed to get BeanValidator to work with Selectable/MultiSelect?
Please advice
Thanks!
You'll need to add the validators to the editable fields themselves, not to the table. (Table itself is a field => the Validator from table.addValidator validates the value of the Table => the value of the table is the selected itemId(s) => the BeanValidator fails)
You can add the validators to the fields that by using a custom TableFieldFactory on the table. Here's a very simple one-off example for this scenario - clearly, if you need to do this with a lot of different beans/tables, it'll be worth creating a more generic/customizable factory
table.setTableFieldFactory(new DefaultFieldFactory() {
#Override
public Field<?> createField(Item item, Object propertyId, Component uiContext) {
Field<?> field = super.createField(item, propertyId, uiContext);
if (propertyId.equals("firstName")) {
field.addValidator(new BeanValidator(Person.class, "firstName"));
}
if (propertyId.equals("lastName")) {
field.addValidator(new BeanValidator(Person.class, "lastName"));
}
if (propertyId.equals("description")) {
field.addValidator(new BeanValidator(Person.class, "description"));
}
return field;
}

smartgwt listgrid set cursor to hand over an icon field

I've been working on this problem for quite a while but have not been able to solve it.
I have a listgrid with a field type icon. I would like to change the cursor to "hand" over the icon.
I've been searching the web and saw that a couple of solutions existed.
One of them is using addCellOverHandler for the list grid. But I don't understand how you can change the cursor for the specified field of the listgrid.
this.addCellOverHandler(new CellOverHandler() {
#Override
public void onCellOver(CellOverEvent event) {
// not able to get the field and setCursor()
}
});
My field in the listgrid is defined as:
ListGridField iconField = new ListGridField("icon");
iconField.setAlign(Alignment.CENTER);
iconField.setType(ListGridFieldType.ICON);
iconField.setIcon("icons/icon.gif");
Like someone pointed out on the forum, a setCursor() method exist for the listgrid, but not for the field only...
If anybody has a clue...
Thanks
After some more (a lot more...) googling, I found this:
http://forums.smartclient.com/showthread.php?t=15748
The thing is to Override the getCellStyle method in the listgrid.
Here is the code I use:
#Override
protected String getCellStyle(ListGridRecord record, int rowNum, int colNum) {
if (colNum==6){
return "EC_pointer";
}
return super.getCellStyle(record, rowNum, colNum);
}
and in my CSS file:
.EC_pointer {
cursor: pointer;
}
The major fallout is that you have to know in advance the column number of the field.
Further to my comment and adding information from here I tested the following code which works with SmartGwt2.4 under Firefox 5.0.
demandesGrid.setCanHover(true);
demandesGrid.setShowHover(false);
demandesGrid.addCellHoverHandler(new CellHoverHandler() {
#Override
public void onCellHover(CellHoverEvent event) {
if (event.getColNum() == demandesGrid.getFieldNum("icon")) {
// SC.say(demandesGrid.getChildren()[3].toString());
demandesGrid.getChildren()[3].setCursor(Cursor.POINTER);
} else {
demandesGrid.getChildren()[3].setCursor(Cursor.DEFAULT);
}
}
});
I don't know if the index of the ListGridBody is constant; I found it with the SC.say line.
How about
grid.addCellOverHandler(new CellOverHandler() {
#Override
public void onCellOver(CellOverEvent event) {
//cellOver event to get field and refresh the cell
//grid.refreshCell(i, j);
}
});
The best approach is fully demonstrated here (take a look at how "comments/stats" field is being initialized).
In short, u have to extend ListGrid and override createRecordComponent method. In this method you can make any custom component you like and it will be show in grid cell.
Also ListGrid should be initialized with:
listGrid.setShowRecordComponents(true);
listGrid.setShowRecordComponentsByCell(true);

Resources