How could building CRUD user interfaces with Vaadin become easier?

Hello all,

We are planning to revamp how you make CRUD user interfaces with Vaadin (both Flow and Hilla). This would include aspects such as master-detail layouts, data binding, validation, etc.

The intention is to provide all the necessary building blocks to build modern, good-looking and accessible CRUD user interfaces.

Now we would very much like to hear your opinions:

  • What do you find difficult or confusing when building CRUD user interfaces with Flow or Hilla today?
  • What features that are missing today would make your life easier when building CRUD user interfaces?
  • What features that exist today do you definitely want to keep using when building CRUD user interfaces?
  • Anything else on your wishlist?

If you are interesting in following the progress of this project more closely, let me know that as well.

-Petter-

(Edit: Changed the topic title to avoid the misunderstanding of this topic being about the CRUD or AutoCRUD components.)

4 Likes

I’m going to answer my own questions as well. These comments apply to Flow:

  • I would like to use Java records with binder.
  • I would like to build user interfaces using MVVM where individual properties are bound and validated as opposed to complete forms.
  • I would like to distinguish between errors and warnings when validating fields.
  • I don’t want to use Java Bean Validation.
  • I would like an easy way of using URL parameters to navigate the CRUD (like selecting items and tabs).
  • I would like good-looking, animated master-detail layouts where I can implement the master and detail parts myself.

Isn’t that already possible? Or do you think it’s not easy to use?

It is possible, but has not yet been implemented in the components so that the messages show up differently depending on whether they are warnings or errors.

Ahh! You are right. I had a similar discussion recently with Teppo here: fix: Binder should not set non-error fields invalid by tepi · Pull Request #18805 · vaadin/flow · GitHub - might be interesting in this topic.

Back to the main topic:

I would personally be against yet-another-CRUD-component-to-rule-them-all, because people are always wanna customize it to their liking and this drastically increases the complexity of said component. I love the current component-way of thinking as “buildling blocks”, but often people are missing “real life” examples how to use them.

2 Likes

@knoobie, I think “CRUD 2.0” is more of a project name, not necessarily implying just a new version of the CRUD component.

Could be also include that, but I think we want to continue more along the lines as you point out, with reusable pieces that you can assemble yourself when needed. The CRUD component (and AutoCRUD, etc) should be more about pre-assembled blocks that sometimes are enough.

1 Like

Records are immutable data carriers. IMO they are not intended to use on two way data bindings

Exactly. I want to use them to carry data to and from the binder. For two-way data bindings, I’d rather use something like MVVM than the binder (which I consider to be one-way).

The point is that I’m usally intereste in which field has changed. And this is only possible with a mutable object

Where would you need to know this? In a backend service after you have “submitted” the form?

Yes for example. See: Binder: Add method to write only the changed properties to the bean · Issue #18583 · vaadin/flow · GitHub

1 Like

I had the same need in the past for highly audited software, where each change has to be confirmed by the user and later persisted in another table. So once a user clicks “Send” he gets a dialog with a table showing him all fields he has changed to confirm everything is correct; afterwards it’s stored on the primary entity and also as audit trail.

2 Likes

I would like to support this! Thank you @Petter for taking the initiative and for talking about it here!

1 Like

My flow would be.

1Âş - Create a generic grid with buttons to delete and update rows, using default methods in interface to reuse code.

Some like that:

public class BaseGrid extends Grid {

public interface GridItemActionListener<T> {
    void edit(T item);
    void delete(T item);
}

public BaseGrid() {
    this.getColumns().forEach(row-> row.setAutoWidth(true));
    this.setSizeFull();
}

public BaseGrid(Class<T> beanType, boolean autoCreateColumns, GridItemActionListener<T> actionListener) {

    super(beanType, autoCreateColumns);

    Column<T> view = addComponentColumn(item -> createButton(VaadinIcon.EDIT, e -> actionListener.edit(item), ButtonVariant.LUMO_TERTIARY))
            .setHeader("View");
    Column<T> delete = addComponentColumn(item -> createButton(VaadinIcon.TRASH, e -> actionListener.delete(item), ButtonVariant.LUMO_ERROR))
            .setHeader("Delete");
}

private Button createButton(VaadinIcon icon, ComponentEventListener<ClickEvent<Button>> clickListener, ButtonVariant variant) {
    Button button = new Button(new Icon(icon));
    button.addThemeVariants(ButtonVariant.LUMO_ICON, variant, ButtonVariant.LUMO_TERTIARY);
    button.addClickListener(clickListener);
    return button;
}

}

2Âş Starting the same principle, create a base modal dialog to recieve a form with fields to manipulate data.

1 Like

I would like to have more control over which properties are exposed to the frontend / UI, see this article on bean mapping.

Hmm… in Flow only properties you have specified and mapped to a field / column are send to the client - nothing more.

1:
There’s often a need to handle expensive validations in an elegant way. A practical example: it’s fairly simple to check that an e-mail address is in a valid format, but it might be slow to go to the database and check that this particular e-mail address has not been used before in this system. So when you have some kind of CRUD form, you should be able to provide a callback that does a potentially long-lasting validity checking on top of the “this is in the correct format” sanity checks. It could have a built-in loading indicator. If there’s an explicit editor component (like in the current CRUD component). And if this longer validation would happen to fail, you should be able to edit the values again without needing to take another step like reopening a dialog. Last time I looked, this was difficult or impossible to do with the CRUD component (although I do recall there have been some changes which might affect things).

2:
Displaying form-level errors that come from form-level validations should have a single obvious place (that’s configurable if it doesn’t suit your needs). If the errors relate to a specific editor field, it should be obvious which one is at fault. If there’s to be a CRUD component revamp, the error message area should be available in some default location.

3:
The current way of dealing with form-level validation errors via binder is hard to discover and the related methods are confusing. There’s:

  • binder.addValueChangeListener
  • binder.addStatusChangeListener
  • binder.setBindingExceptionHandler
  • binder.setValidationErrorHandler
  • binder.setValidationStatusHandler

Which one do I use for what again?

4:

It should be easy to toggle validations and required status for Fields. For example: If you choose you’re travelling abroad, you must supply your travel insurance information. If you’re travelling domestically, travel insurance is optional.

Comment for earlier question about warnings vs. errors.

One potential workaround with current Binder is to use two Binders. Use the other one to guard errors and the other one just to highlight warnings. In the second Binder you can use withValidationStatusHandler to display the warnings in some other place than regular error message. E.g. you can use helper text if that is available. Or you can use regular error text and set specific class name to component for different styling. In Vaadin 24 there are warning colors in the palette of predefined colors.

I would like to build user interfaces using MVVM where individual properties are bound and validated as opposed to complete forms.

I would assume that there is no strict relationship between MVVM and whether fields are bound individually or as form.

There is actually an API in Binder for that. In binding there are methods to toggle validation and asRequired on/off.

In general the points 1. - 4. in Olli’s post are good example of that in addition to functional binding aspects, there is also UX/design aspects that could be simplified. Although it is somewhat trivial to add Div component to your form, and set its text to show bean level error and even LumoUtility has predefined classes you can use in that Div to make it look fine, it is something that wont pop into mind easiely. We could have more this kind of design functionality in better FormLayout component. This would naturally mean that the design choices would be mostly done by us following the “most common” patterns we see in discussions and our customer’s projects. If the application will have very different design, the fall back is always to build the form layout yourself using more elementary components, and e.g. those LumoUtility classes.

Btw. regarding the loading / waiting spinner, there is actually one built-in Lumo, but it is not easy to find, thus I wrapped it in component: Spinner - Vaadin Add-on Directory