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.
Crud UI Add-on
Hello everyone!
I've implemented a new Vaadin add-on to generate CRUD-like interfaces at runtime:
https://vaadin.com/directory#!addon/crud-ui-add-on
This add-on defines a set of interfaces that are useful to implement CRUD-like UIs. There are at least one implementation for each interface, but I'm planing to implement new ones. You can also implement your own (and contribute them?).
Supose you have a User class (a Java bean) and you want to show a CRUD UI for it. All you have to do is something like this:
GridBasedCrudComponent<User> crud = new GridBasedCrudComponent<>(User.class);
layout.addComponent(crud);
// use lambda expressions or method references to delegate CRUD operations to your backend:
crud.setAddOperation(user -> backend.add(user));
crud.setUpdateOperation(backend::update);
crud.setDeleteOperation(backend::delete);
crud.setFindAllOperation(backend::findAll);
See the examples provided in the add-on page for more advanced use cases.
Issues and code contributions are welcome on GitHub: https://github.com/alejandro-du/crudui
Hope you find it useful!
Hi Alejandro, I used the updated version of crudui add-on. Thank you for the improvements. I would like some recommendations to :
- prevent adding an empty record;
- send a confirmation request before a delete operation;
- treat a relationship type fields 1: n as CustomerStatus whose selection comboBox is fed from another table;
- specify the date format (dd / mm / yyyy) to the grid and the form.
regards,
Hi, thanks for the feedback! I uploaded a new version (1.3) of the add-on with some nice improvements.
Eric,
"- prevent adding an empty record;"
I'm not sure what you mean. Would you mind elaborating?
"- send a confirmation request before a delete operation;"
The window that is shown when the "x" button is clicked is the confirmation request. Otherwise the user would have to click "x", then click "Delete", then click "Yes, I confirm", which sounds like too much. Maybe using a "Confirm deletion" title for the window would make things clearer.
"- treat a relationship type fields 1: n as CustomerStatus whose selection comboBox is fed from another table;"
With version 1.3 this is doable by using the setFieldType and setFieldCreationListener methods. For example:
crud.setFieldType("mainGroup", ComboBox.class);
crud.setFieldCreationListener("mainGroup", field -> {
ComboBox comboBox = (ComboBox) field;
comboBox.setContainerDataSource(new BeanItemContainer<>(Group.class, groups));
comboBox.setItemCaptionPropertyId("name");
});
"- specify the date format (dd / mm / yyyy) to the grid [...]"
This is doable by setting a DateRenderer for the corresponding column in the Grid. For example:
crud.getGrid().getColumn("birthDate").setRenderer(new DateRenderer("%1$te/%1$tm/%1$tY"));
"[...] and the form."
With version 1.3 you can use the setFieldCreationListener method to configure the fields. For example:
crud.setFieldCreationListener("birthDate", field -> ((DateField) field).setDateFormat("yyyy-MM-dd"));
Happy coding!
Hi Alejandro. Thanks for a really useful add-on.
Works great. How would this Crud UI work or a java bean that has a collection of objects and would allow me to edit the collection of items in the form popup?
Thank you kindly,
Jason
Hi Alejandro and thanks thanks for the enhancements in crudui add-on version 1.3.
Date format now is working greats!
crud.getGrid().getColumn("birthDate").setRenderer(new DateRenderer("%1$te/%1$tm/%1$tY"));crud.setFieldCreationListener("birthDate", field -> ((DateField) field).setDateFormat("dd/MM/yyyy"));
"- prevent adding an empty record;"Alejandro: “I'm not sure what you mean. Would you mind elaborating?”
In Add form, there’s no required field thus when you clic “Save” with any value, empty record is added in the Grid with only Id! (See attached images).
"- send a confirmation request before a delete operation;"
Alejandro, we know that deletion is a critical operation, and we must care about that by requiring a confirmation by a clear message from the user. You may provide it even as optional;
"- treat a relationship type fields 1: n as CustomerStatus whose selection comboBox is fed from another table;"
For 1.3 version, when i use:
crud.setFieldType("status", ComboBox.class);crud.setFieldCreationListener("status", field -> { ComboBox comboBox = (ComboBox) field; comboBox.setContainerDataSource(new BeanItemContainer<>(CustomerStatus.class, EnumSet.allOf(CustomerStatus.class))); comboBox.setItemCaptionPropertyId("status");});
Combobox adds lines the right number lines but does not display any value. When you edit a customer for example, the corresponding line is display but not item showed! (see attached image).
Regards
Hi Jason,
If by "edit the collection" you mean that the end user can modify the "selected values" then it's quite easy to do:
crud.setFieldType("groups" OptionGroup.class);
crud.setFieldCreationListener("groups", field -> {
OptionGroup optionGroup = (OptionGroup) field;
optionGroup.setMultiSelect(true);
optionGroup.setContainerDataSource(new BeanItemContainer<>(Group.class, groups));
optionGroup.setItemCaptionPropertyId("name");
return optionGroup;
});
BTW, in version 1.5, I'll add a more convenient setFieldProvider method.
Many thanks Alejandro. That answer worked well.
How about using a custom field for an entity with a nested object?
I tried:
GridBasedCrudComponent<Person> crud = new GridBasedCrudComponent<>(Person.class);
crud.setFieldType("address", AddressPopup.class);
public class Person {
private String firstName;
private String lastName;
private Address address;
//getters and setters
public class Address {
private String street;
private String zip;
//getters and setters//
Taken from https://vaadin.com/wiki/-/wiki/Main/Creating+a+custom+field+for+editing+the+address+of+a+person
public class AddressPopup extends CustomField<Address> {
private static final long serialVersionUID = 2721197776543658373L;
private FieldGroup fieldGroup;
@Override
protected Component initContent() {
FormLayout layout = new FormLayout();
final Window window = new Window("Edit address", layout);
TextArea street = new TextArea("Street address:");
TextField zip = new TextField("Zip code:");
TextField city = new TextField("City:");
TextField country = new TextField("Country:");
layout.addComponent(street);
layout.addComponent(zip);
layout.addComponent(city);
layout.addComponent(country);
fieldGroup = new BeanFieldGroup<Address>(Address.class);
fieldGroup.bind(street, "street");
fieldGroup.bind(zip, "zip");
fieldGroup.bind(city, "city");
fieldGroup.bind(country, "country");
Button button = new Button("Open address editor", new ClickListener() {
/**
*
*/
private static final long serialVersionUID = 2410419047939583633L;
public void buttonClick(ClickEvent event) {
getUI().addWindow(window);
}
});
window.addCloseListener(new CloseListener() {
/**
*
*/
private static final long serialVersionUID = -1936217938466658922L;
public void windowClose(CloseEvent e) {
try {
fieldGroup.commit();
} catch (CommitException ex) {
ex.printStackTrace();
}
}
});
window.center();
window.setWidth(null);
layout.setWidth(null);
layout.setMargin(true);
return button;
}
@Override
public Class<Address> getType() {
return Address.class;
}
@Override
protected void setInternalValue(Address address) {
super.setInternalValue(address);
fieldGroup.setItemDataSource(new BeanItem<Address>(address));
}
}
But received the following exception:
com.vaadin.data.Buffered$SourceException: null
at com.vaadin.ui.AbstractField.setPropertyDataSource(AbstractField.java:667) ~[vaadin-server-7.7.3.jar:7.7.3]
at com.vaadin.data.fieldgroup.FieldGroup.bind(FieldGroup.java:278) ~[vaadin-server-7.7.3.jar:7.7.3]
at com.vaadin.data.fieldgroup.BeanFieldGroup.bind(BeanFieldGroup.java:155) ~[vaadin-server-7.7.3.jar:7.7.3]
at com.vaadin.data.fieldgroup.FieldGroup.buildAndBind(FieldGroup.java:1186) ~[vaadin-server-7.7.3.jar:7.7.3]
at com.vaadin.data.fieldgroup.BeanFieldGroup.buildAndBind(BeanFieldGroup.java:162) ~[vaadin-server-7.7.3.jar:7.7.3]
at org.vaadin.crudui.AbstractAutoGeneratedCrudFormFactory.addFields(AbstractAutoGeneratedCrudFormFactory.java:26) ~[crudui-1.4.jar:1.4]
at org.vaadin.crudui.impl.form.GridLayoutCrudFormFactory.buildNewForm(GridLayoutCrudFormFactory.java:33) ~[crudui-1.4.jar:1.4]
at org.vaadin.crudui.impl.crud.GridBasedCrudComponent.buildForm(GridBasedCrudComponent.java:218) ~[crudui-1.4.jar:1.4]
at org.vaadin.crudui.impl.crud.GridBasedCrudComponent.addButtonClicked(GridBasedCrudComponent.java:165) ~[crudui-1.4.jar:1.4]
at org.vaadin.crudui.impl.crud.GridBasedCrudComponent.lambda$new$63983b74$2(GridBasedCrudComponent.java:57) ~[crudui-1.4.jar:1.4]
What am i doing wrong? Many thanks
Hi Jason, it should work by calling formFactory.setFieldProvider("address", () -> new AddressPopup()); in version 1.5. which I'll publish later this week.
Hi Alejandro, I tried the crudui add-on 1.4 and it works very well for me, thanks.
"- treat a relationship type fields 1: n as CustomerStatus whose selection comboBox is fed from another table;"
For 1.4 version, i used:
crud.getGridContainer().addNestedContainerBean("country"); crud.getGrid().setColumns("firstName", "lastName", "birthDate", "email", "gender", "country.name"); ... // Rename column grid headers crud.getGrid().getColumn("birthDate").setHeaderCaption("Date of birth"); crud.getGrid().getColumn("gender").setHeaderCaption("Gender"); crud.getGrid().getColumn("country.name").setHeaderCaption("Country"); // customize fields: crud.getGrid().getColumn("birthDate").setRenderer(new DateRenderer("%1$te/%1$tm/%1$tY")); crud.setFieldCreationListener("birthDate", field -> ((DateField) field).setDateFormat("dd/MM/yyyy"));
// enum set for gender {Male, Female}
crud.setFieldType("gender", ComboBox.class);
crud.setFieldCreationListener("gender", field -> {
ComboBox comboBox = (ComboBox) field;
comboBox.setContainerDataSource(new BeanItemContainer<>(CustomerGender.class, EnumSet.allOf(CustomerGender.class)));
});
// entity class for CustomerCountry
CountryService countryService = CountryService.getInstance(); crud.setFieldType("country", ComboBox.class); crud.setFieldCreationListener("country", field -> { ComboBox comboBox = (ComboBox) field; comboBox.setItemCaptionPropertyId("name"); comboBox.setContainerDataSource(new BeanItemContainer<>(Country.class, countryService.findAll(""))); });
It works greats! (see attached image).
Still have theses requests
"- prevent adding an empty record;"
Alejandro: “I'm not sure what you mean. Would you mind elaborating?”
In Add form, there’s no required field thus when you clic “Save” with any value, empty record is added in the Grid with only Id! (See attached images).
"- send a confirmation request before a delete operation;"
Alejandro, we know that deletion is a critical operation, and we must care about that by requiring a confirmation by a clear message from the user. You may provide it even as optional;
Eric, thanks for the feedback.
The empty record could be a legit data entry, so I cannot add such validation to the add-on. However in 1.5 you'll be able to throw a CrudOperationException from the CRUD listeners to indicate an invalid data entry. The exception's message is shown as an error on the form.
Regarding the delete confirmation, as I said, the window shown when the "x" button is clicked is the confirmation request itself. I'll change the default title of the form/window and button in the delete forms for version 1.5 so that the confirmation request is clear. However, if you still want to have two confirmations, you can extend the VerticalCrudFormFactory or GridLayoutCrudFormFactory classes and override the buildButton() to build a button that handles the additional confirmation (this method will be available in 1.5).
Hello everybody,
I just published version 1.5 with the promised enhancements :D The API has changed so, please have a look at the examples on the add-on page. Any further ideas and feedback on how to improve it are apreciated.
Hi Alejandro, I tried the crudui add-on 1.5 and thanks for the work done. I've encountered some difficulties while migrating theses codes:
// 1. enum set for gender {Male, Female}
Replacing
crud.setFieldType("gender", ComboBox.class);
crud.setFieldCreationListener("gender", field -> {
ComboBox comboBox = (ComboBox) field;
comboBox.setContainerDataSource(new BeanItemContainer<>(CustomerGender.class, EnumSet.allOf(CustomerGender.class)));
});
By
formFactory.setFieldType("gender", ComboBox.class);
formFactory.setFieldCreationListener("gender", field -> {
ComboBox comboBox = (ComboBox) field;
comboBox.setContainerDataSource(new BeanItemContainer<>(CustomerGender.class, EnumSet.allOf(CustomerGender.class)));
});
Do not fill data anymore!
// 2. entity class for CustomerCountry
Replacing
crud.setFieldType("country", ComboBox.class);
crud.setFieldCreationListener("country", field -> {
ComboBox comboBox = (ComboBox) field;
comboBox.setItemCaptionPropertyId("name");
comboBox.setContainerDataSource(new BeanItemContainer<>(Country.class, countryService.findAll("")));
});
By
formFactory.setFieldType("country", ComboBox.class);
formFactory.setFieldCreationListener("country", field -> {
ComboBox comboBox = (ComboBox) field;
comboBox.setItemCaptionPropertyId("name");
comboBox.setContainerDataSource(new BeanItemContainer<>(Country.class, countryService.findAll("")));
});
Raised error:
GRAVE: com.vaadin.data.fieldgroup.FieldGroup$BindException: Unable to build a field of type com.vaadin.ui.ComboBox
for editing com.example.tutorial.backend.Country
// 3. How to use setFieldProvider for ComboBox field ?
formFactory.setFieldProvider("groups", () -> {
OptionGroup optionGroup = new OptionGroup();
optionGroup.setMultiSelect(true);
optionGroup.setContainerDataSource(new BeanItemContainer<>(Group.class, groups));
optionGroup.setItemCaptionPropertyId("name");
return optionGroup;
});
What am i doing wrong? Many thanks
Check that you are setting the CrudFormFactory to the CrudComponent. Also, use setFieldProvider to create custom fields. When adding enum values, use Arrays.asList(TheEnum.values()) insted of EnumSet.
See this example: https://github.com/alejandro-du/crudui/blob/master/src/test/java/TestUI.java
Thanks Alejandro, it s now working!
1. "- send a confirmation request before a delete operation;"Alejandro, we know that deletion is a critical operation, and we must care about that by requiring a confirmation by a clear message from the user. You may provide it even as optional;
It is better now with version 1.5!
However can I set the caption of the corresponding message <Are you sure you want to delete this item?> ?
2. "- prevent adding an empty record;"
Alejandro: The empty record could be a legit data entry, so I cannot add such validation to the add-on. However in 1.5 you'll be able to throw a CrudOperationException from the CRUD listeners to indicate an invalid data entry. The exception's message is shown as an error on the form.
How can I do this? Suppose I want First Name and Birth Date set to required.
3. formFactory.setDisabledPropertyIds(CrudOperation.UPDATE, "id"); not seems to have effect anymore. I want for example to set Id field visible but disabled during UPDATE operation.
4. How to set refresh list visible, someting like: crud.setXxxxVisible(Boolean); ?
What am i doing wrong? Thanks
Eric,
1. Captions and window titles are up to the FormLayout implementation: WindowBasedCrudLayout.setWindowCaption or HorizontalSplitCrudLayout.setFormCaption.
2. Use the Bean Validation API or manually add Validators.
3. Will be fixed in 1.5.1.
4. crud.setFindAllOperationVisible(Boolean).
Hi Alejandro,
Could build a "ReportViewer add-on" which will allow to preview a report in a new Form and provides the following boutons/links operations: Print, ExportToPDF, ExportToExcel, ExportToWord and ExportToCSV ?
And the ability to communicate with a backend.
Best regards
Hi, that's on my to-do list ;) I don't know when I'll have time for it, though. But hey, let's use this thread to solve things related to the Crud UI add-on :)
You're right, in fact it's just because I haven't got any feedback for this request in private message!
Regards
Hi Alejandro. Thanks for the improvements to 1.5
I've found everything super simple to customise with apart from the cases of domain objects with nested objects with multiple fields (e.g. CRUD for a person object who has many addresses.
Can you advise on how i would go about creating forms for this sort of relationship:
class Person{
String name;
String lastname;
List<Address> addresses;
}
I've tried using the formFactory.setFieldProvider to assign a custom popup window for filling in an address like so:
GridLayoutCrudFormFactory<Person> formFactory = new GridLayoutCrudFormFactory<>(Person.class, 2, 2);
GridBasedCrudComponent<Person> crud = new GridBasedCrudComponent<>(
Person.class);
crud.setCrudFormFactory(formFactory);
formFactory.setFieldProvider("address", ()-> new AddressPopup());
addComponent(crud);
but the result is a IllegalArgumentException: Window is already attached to an application. So I'm clearly doing something wrong!
Thank you kindly,
Jason
Thanks Alejandro. You were quite right - I was doing exactly that.
I still receive an exception using the above custom field but leaving that aside ...
whats the expected way of using the form factory to create nested forms for nested entities - such as an address in a person object.
many thanks,
Jason
Jason, if you mean CrudFormFactory, then adding a field provider is the way to go.
Hi Alejandro, now that the crud ui add-on has been stabilized, could you look into add-on reports as promised? In case you do not have time, how could we migrate the enterprise-app report module?
Regards
@Alejandro - how do you feel about support for paging of db result sets?
Jason, I don't want to couple the add-on to specific persistence technologies. All the interaction with the actual data is delegated to crud listeners, those can handle pagination or any other data-level details
Hi Alejandro
I am following the examlpes that you provided in the Testui. I would like to extend the formlayout so that if I throw a crudOperationException the error that I provide should be displayed on the existing form. Currently what happens is the the edit/add form is hidden/removed and then notification is displayed on the grid.
When adding/ editing the row in question again, the previously captured form details are still there. I would then like to focus the field the caused the error as well
Go ahead! You may want to contribute that implementation to the add-on on GitHub through a push request.
Hi Alejandro,
Do you have any examples of how to use the crud form with bean validation so it doesn't close if there's an error and waits till the user fixes the error?
Thanks!
Hi Arindam,
I'm not sure what you mean. Is the modal window closing for you somehow? It works okay for me without having to do much.
Alejandro Duarte: Hi Arindam,
I'm not sure what you mean. Is the modal window closing for you somehow? It works okay for me without having to do much.
Hey Alejandro,
So if some of my bean fields are marked with @NotNull or other validation specs and these fields are not filled in or filled in correctly how would i prevent the form from being closed? I want the user to be able to fix their entries and then try to create/update the pojo.
Thanks
It should work. Users are able to fix the validation errors. Did you add the Bean Validation dependency to your project?
Alejandro Duarte: It should work. Users are able to fix the validation errors. Did you add the Bean Validation dependency to your project?
so far I have the following,
<dependency org="javax.validation" name="validation-api" rev="1.1.0.Final"/>
<dependency org="org.hibernate" name="hibernate-validator-annotation-processor" rev="5.2.1.Final"/>
<dependency org="org.hibernate" name="hibernate-validator" rev="5.3.4.Final"/>
and other logging deps
Hibernate Validator requires an EL implementation, did you add those dependencies as well?
Alejandro Duarte: Hibernate Validator requires an EL implementation, did you add those dependencies as well?
yes, I followed the instructions on http://hibernate.org/validator/documentation/getting-started/
<dependency> javax.el-javax.el-api:2.2.4 </dependency>
<dependency>org.glassfish.web-javax.el:2.2.4</dependency>
<dependency>org.hibernate-hibernate-validator-cdi:5.3.4.Final</dependency>
The current behavior is if i leave all the fields empty and hit 'Add' it says 'Item saved' and closes the dialog
Well, it works for me. Are you able to provide a minimal project I can compile and execute to reproduce the issue?
Alejandro Duarte: Well, it works for me. Are you able to provide a minimal project I can compile and execute to reproduce the issue?
Creating a minimal project isolated the issue. I was using Immutables to generate my pojos and that didn't play well with the validation annotations for some reason. hand coding them has fixed the issue. thanks again for your help, I appreciate it!
Hi.
I noticed strange behaviour, after updating the record. Update is executed to database, but grid record stays unselected, even stanger, I can not select updated record any more.
I've checked database record, it's updated.
I have BaseEntity as superclass for all entities. I suspect equals method or maybe lombok.
Any clues?