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.

report enabled
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