Getting Started
linkki Example
Goal
The goal is to create a simple user interface for reporting errors. The report should have a description and a type. The following types should be available:
-
Bug
-
Improvement
-
Question
The report can only be submitted if a description has been entered and a type was selected.
Persisting the report is not part of this tutorial. |
Prerequisites
To start this project, follow the steps described in project setup.
Step 1: Model
First we implement the 'DomainModel'. The model consists of two classes, Report
and ReportType
. These classes do not receive UI specific annotations.
public class Report {
private Integer id;
private String description;
private ReportType type;
// getters and setters
}
public enum ReportType {
BUG,
IMPROVEMENT,
QUESTION;
public String getName() {
String name = name();
return name.substring(0, 1) + name.substring(1).toLowerCase();
}
}
ReportType implements the method getName() . This method is called by linkki to display the 'caption' for the enum values.
|
Step 2: PresentationModelObject (PMO)
Our UI shall receive a text area for the entry of a description. For the selection of the report type a ComboBox with the enum constants of the ReportType
shall be displayed. To submit the report we also require a Send
button. It shall only be active if a description has been entered and a type was selected.
Now we create a new class in the package org.linkki.samples.gettingstarted.pmo
, naming it ReportSectionPmo
. Of importance is the @UISection
annotation on the class.
The class has an instance variable, the Report
, which must be passed to the constructor.
@UISection
public class ReportSectionPmo implements PresentationModelObject {
private final Report report;
public ReportSectionPmo(Report report) {
this.report = requireNonNull(report, "report must not be null");
}
...
For the model binding to work, a method must be created and annotated with @ModelObject
.
@ModelObject
public Report getReport() {
return report;
}
Now the TextArea and the ComboBox are defined. Again, for each a method must be created, annotated with @UITextArea
and @UIComboBox
, respectively.
@UITextArea(position = 10, label = "Description", modelAttribute = "description", required = RequiredType.REQUIRED, rows = 5, width = "50em")
public void description() {
// Use description from report (model object) directly
}
@UIComboBox(position = 20, label = "Type", modelAttribute = "type", required = RequiredType.REQUIRED)
public void type() {
// - bind value to the property "type" from report
// - use enum constants from ReportType as available values
}
If the methods have the same name as the fields in the domain model, the property modelAttribute can be omitted.
|
The annotations must always be added to the methods, not the fields of the class. |
The last step is the definition of the button. As a special feature, the button shall be activated or not depending on values in the model. This is determined via the property enabled = EnabledType.DYNAMIC
.
By setting EnabledType.DYNAMIC
, linkki searches for a method boolean isSendEnabled()
. It is called automatically during the update of the UI. Its return value determines the activation state of the button.
The method name must conform to the convention is[NameOfThePropertyBelongingToTheAnnotatedMethod]Enabled(). The method must be public and return a boolean value. |
@UIButton(position = 30, caption = "Send", icon = VaadinIcons.PAPERPLANE, showIcon = true, enabled = EnabledType.DYNAMIC)
public void send() {
report.save();
Notification.show(
String.format("Report with id %d filed!", report.getId()),
"Thank you for reporting!",
Notification.Type.TRAY_NOTIFICATION);
}
/**
* Enable button only if description and type is present.
*
* @return {@code true} if button is enabled otherwise {@code false}
*/
public boolean isSendEnabled() {
String description = report.getDescription();
return description != null && !description.isEmpty()
&& report.getType() != null;
}
Step 3: Vaadin UI
The UI contains 'only' the section for the creation of the report.
The first part is 'normal' Vaadin code. The interesting linkki parts start at (2)
. First a PmoBasedSectionFactory
is instantiated. The PMO is passed as parameter to the method createSection
. As second parameter a BindingContext
is passed → it is explained in the later chapters. As the last step the created section
must now be set as content
of the UI.
@Theme(value = "valo") (1)
public class GettingStartedUI extends UI {
private static final long serialVersionUID = 1L;
@Override
protected void init(VaadinRequest request) {
Page.getCurrent().setTitle("linkki :: Getting Started");
(2)
PmoBasedSectionFactory sectionFactory = new PmoBasedSectionFactory();
AbstractSection section = sectionFactory.createSection(new ReportSectionPmo(new Report()),
new BindingContext("report-context",
PropertyBehaviorProvider.NO_BEHAVIOR_PROVIDER,
Handler.NOP_HANDLER));
setContent(section);
}
}
1 | Standard Vaadin code |
2 | linkki specific code |
Step 4: web.xml
The application must be made known to the application server. It expects a web.xml
, in which our UI is started with the VaadinServlet
.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>Linkki-Getting-Started</display-name>
<distributable/>
<servlet>
<servlet-name>Vaadin Application Servlet</servlet-name>
<servlet-class>com.vaadin.server.VaadinServlet</servlet-class>
<init-param>
<param-name>UI</param-name>
<param-value>org.linkki.samples.gettingstarted.GettingStartedUI</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Vaadin Application Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Starting the Application
The application can be started from the command line via
$> mvn clean install tomee:run
and is then reachable at http://localhost:8080/linkki-getting-started.