There has previously been a discussion about “Declarative UI with XML” (see
http://vaadin.com/forum/-/message_boards/message/96357
), but as we currently are working with a more static UI which uses type-safe databinding (I will post another message about how we could build this natively into Vaadin soon), we really prefer to do our UI in Java.
However, the Vaadin API isn’t that elegant when it comes to configuring it. It doesn’t allow for method chaining or a fluent configuration interface. It is luckily on the roadmap for Vaadin 7 (see
http://vaadin.com/web/joonas/wiki/-/wiki/Main/Vaadin_7_API
), but it’s not here and we needed a fluent interface for configuring our Vaadin components.
There are several approaches to doing a fluent interface and we’ve decided to go for a Builder pattern (means we don’t have to interfere with Component inheritance, etc.).
Since there’s quite a few Vaadin components we’ve approached the problem from a code generation perspective, since this is one of our specialities (see
TigerMDSD
). Had we been using for instance Scala, we could have done most of this using Implicits and Compiler extensions, but we’re currently targeting Java.
A generated approach will never be as nice a carefully handcraftet version, but then again, we did this solution in less than 4 hours.
So first, let’s see what the code using Builders looks like when configuring a horizontal panel with two buttons which are added to a Form’s layout (the form was already created)?
import static com.vaadin.ui.Builders.*;
form.getLayout().addComponent(
horizontalLayout()
.spacing(true)
.addComponents(
button(text("general.button.back"), new Button.ClickListener() {
public void buttonClick(Button.ClickEvent event) {
form.discard();
getViewManager().showView(HomeView.class);
}
}),
button(text("general.button.save"), new Button.ClickListener() {
public void buttonClick(Button.ClickEvent event) {
form.commit();
User user = getFormEntity();
getPersistenceManager().merge(user);
getViewManager().showView(HomeView.class);
}
}
)
).getComponent());
The approach that we went for is to create a Builder for every concrete, public and not deprecated Vaadin component.
So for a HorizontalLayout there will be generated a HorizontalLayoutBuilder, which
for every set(value) in HorizontalLayout has a matching (value) method.
Every method in the HorizontalLayoutBuilder returns this, i.e. HorizontalLayoutBuilder, so it allows for call chaining.
For methods which maps a 1-m association but only allow you to specify one assocation at a time, e.g. addComponent(Component), we create a
method which allows you to provide more than one value.
So when HorizontalLayout has a “void addComponent(Component component)” then HorizontalLayoutBuilder
will have a “HorizontalLayoutBuilder addComponents(Component…components)” method (i.e. vararg method).
When you’re done setting up a component tree, you just call “.getComponent()” on the outermost builder
and you’re done.
So how does that work?
The generator knows which Vaadin components map to which builders, so it will actually create
shadow property setter or add methods which accept Builder(s) instead of Component instances.
This way you can avoid having to call getComponent() more than once.
The final icing on the cake is a Builders class, which contain static initializer methods, which
you can statically import in your Views/Windows, so you can type horizontalLayout() instead of new HorozontalLayoutBuilder()
Btw. we also handle any constructors that are defined in a Component and provide you with an equivalent
version in the components Builder as well as in the Builders class, hence the ability to call button(“caption”, new Button.ClickListener() {…})
I’ve attached the raw generated code (without any comments) for those interested - generated against version 6.2.0 components.
What’s your take on this?
If there’s enough interest I would be willing to add and maintain this in the incubator/addons until Vaadin 7.0 comes along.
/Jeppe
11151.jar (56.3 KB)