Purpose of this project is to define a declarative XML-based syntax for defining Vaadin user interfaces. Also another goal is to provide support annotation-based binding of data sources and event handlers. Starting from version 0.5.0 Clara supports only Vaadin 7. A separate branch exist for Vaadin 6, but it is not maintained anymore.
Project also serves as a part of my Master's thesis at the University of Turku and also derives from the work done by Joonas Lehtinen on his [xmlui Vaadin add-on](http://vaadin. com/addon/xmlui). A lot of the functionality is also inspired by GWT UiBinder.
Maven is used to build the add-on and the demo application modules (thanks to vdemeester for help). Travis CI is used for automated testing.
To package and install the Clara add-on to your local repository, just run the following command:
mvn install
If you want to run and/or package the demo application, you must also compile the widgetset.
cd clara-demo
mvn vaadin:compile jetty:run
Packaging the distributable add-on zip (that can be uploaded to Vaadin Directory) can be done as follows.
cd clara
mvn clean package assembly:single
The project is licensed under the Apache License, Version 2.0.
A good starting point to try things out is the online demo application demonstrating the current version of the project.
- claraxsd-maven-plugin – A Maven plugin to generate XML schemas to enable code completion and validation.
See the forked SimpleAddressbook example that is modified to use Clara.
Quickstart is written for Clara 1.0.0.
-
Create a new Vaadin 7 project.
-
Download the latest version of Clara to WEB-INF/lib from Vaadin Directory (or use the Maven dependency).
-
Create a new Java package that will contain the XML layout definition and the controller class (see steps 4 and 5).
-
Create a new XML file (name it
MyFirstClaraLayout.xml
) for the layout definition and save it to the Java package you just created. You can copy the example below. Notice the special "urn:vaadin:parent" namespace on the componentAlignment attribute which means that the componentAlignment property belongs to the containing layout instead of the component itself.
<?xml version="1.0" encoding="UTF-8"?>
<VerticalLayout xmlns="urn:import:com.vaadin.ui" xmlns:l="urn:vaadin:parent">
<Label id="my-label" value="Hello Clara!" />
<Button id="my-button" caption="Click me!" width="200px" l:componentAlignment="MIDDLE_CENTER" />
</VerticalLayout>
- Create a plain old Java class with the name
MyFirstClaraController
and add it to the same Java package as the XML file. Binding this controller to the components in the XML layout is done with annotations. Add the following two methods to your controller class.
// The value "my-button" of the annotation is a reference to the id attribute in the XML layout.
@UiHandler("my-button")
public void handleMyButtonClick(ClickEvent event) {
Notification.show("Clicked!");
}
@UiDataSource("my-label")
public Property<String> getLabelProperty() {
return new ObjectProperty<String>("Hello from Controller!",
String.class);
}
By using the @UiField
annotation on a field, you could bind a field directly to a component instance with a certain id. For example:
@UiField("my-label")
private Label myLabel;
- Now you can instantiate the XML definition to a Vaadin component in your Java code. See the example below.
// Use the static "create" method to instantiate the XML into a Vaadin component.
VerticalLayout layout = (VerticalLayout) Clara.create(
"MyFirstClaraLayout.xml", new MyFirstClaraController());
// Now the layout is ready to be used.
setContent(layout);
- Congratulations, you just created your first application that uses Clara. As next steps you might want to see the other static methods contained in the
Clara
class to see more ways to use Clara.
Attribute filters enable runtime modification of any attributes read from the declarative XML layout file. The most obvious use case for this is to provide internationalization of text displayed in the user interface.
To create an attribute filter, you must implement the single-method AttributeFilter
interface and pass it to the Clara.create
method. The sole method in the interface is called filter
and it takes a single argument of type AttributeContext
. You can modify the value before it's assigned by calling the setValue
method of the AttributeContext
. You should always call the proceed
method to pass the value forward to next filter (or to finally assign the value). If you do not call the proceed
method, the attribute value will never be assigned (which might sometimes be the desired effect).
Simple example of an AttributeFilter
implementation:
AttributeFilter filter = new AttributeFilter() {
@Override
public void filter(AttributeContext attributeContext) {
if (attributeContext.getValue().getClass() == String.class) {
String value = (String) attributeContext.getValue();
if (value.startsWith("{i18n:")) {
String translatedValue = getTranslation(value);
attributeContext.setValue(translatedValue);
}
}
try {
attributeContext.proceed();
} catch (Exception e) {
e.printStackTrace();
}
}
};
Occasionally you need extra control over the creation of the component tree. The class ClaraBuilder
supports adding AttributeFilter
and extra AttributeParser
instances.
You can also set a common prefix that is used for all ids read from the XML definition. The primary use case for setting an id prefix is to be able to generate page-wide unique ids in combination with custom, reusable components that use Clara internally to build themselves; it is advisable to combine this with InflaterListener
.
An example of using ClaraBuilder
:
Clara.build()
.withController(this)
.withIdPrefix("example.")
.withAttributeFilter(new InternationalizationFilter())
.withAttributeParser(new ResourceParser())
.createFrom("example-ui.xml");
When you use an id prefix, the binding with the @UiField
, @UiHandler
and @UiDataSource
still use the id without prefix (that is: the id defined in the XML).
Components can be notified by Clara when they have been fully constructed, all their children have been created and all attributes have been set by implementing org.vaadin.teemu.clara.inflater.InflaterListener
. This allows a component to do additional construction work that requires knowledge of - for example - the attribute values set from XML.
As an example, a custom component that itself uses Clara for its definition could build its component tree from the InflaterListener#componentInflated()
method and use its own id as the id prefix. This can help with creating page-wide unique ids.
An example:
public class SomeCustomComponent extend CustomComponent implements InflaterListener {
@Override
public void componentInflated() {
Component root = Clara.build()
.withIdPrefix(getId())
.withController(this)
.createFrom("SomeCustomComponent.xml")
setCompositionRoot(root);
}
}