This step introduces the concept of a linkki PMO. You will learn how to create a so-called section with such a PMO and the annotation @UISection. Along with that, you will also be introduced to the first component annotations @UITextField and @UIButton.

The linkki archetype already provides the fundamental setup for your BusinessPartnerView. In this step, you will create an input field in the view where the user can enter a search term and trigger the search.

search bar
Figure 1. Search bar and button

Creating the section to enter search criteria

In linkki, each layout element is repesented by a Presentation Model Object (PMO) that is annotated with a layout annotation. The most commonly used layout is the section (@UISection).

To start with the first layout, create a class with the name SearchSectionPmo and annotate it with @UISection.

To implement the text field for search term input, follow the following steps:

  1. Create a new field searchText that stores the user’s input.

  2. Create and implement the getter method for the property searchText with the signature String getSearchText().

  3. Annotate the getter method with @UITextField. The annotation requires a value position which can set to an arbitrary positive integer value, e.g. 10.

  4. Create and implement the setter method for the property searchText. This method should have the signature public void setSearchText(String).

The finished implementation looks like this:
public class SearchSectionPmo {
    private String searchText = "";

    @UITextField(position = 10, label = "")
    public String getSearchText() {
        return searchText;

    public void setSearchText(String searchText) {
        this.searchText = searchText;
Just like layouts, input components are also represented by annotations. The built-in annotations cover the components that are most commonly used in a form. It is also possible to create your annotations for custom components.

The UI element UITextField is declared via the annotation @UITextField. It is followed by two properties: position and label.

The attribute position defines the display order of the components inside of a layout.

The getter method that is annotated with UI element annotation determines what is displayed in the field. The setter method defines what happens with the user input. The mechanism of a PMO is further explained in the documentation.

In summary, whenever the user input changes, the setSearchText method will be called, including the actual user’s input as argument. Subsequently, the text field will display the return value from the getSearchText method.

The following image illustrates the data binding for the property searchText:

data binding
Figure 2. Data binding

To additionally display a button to trigger the search, follow the following steps:

  1. Create a new field searchConsumer of type Consumer<String>.

  2. Create a constructor with the signature SearchSectionPmo(Consumer<String>) which assigns the argument to the field searchConsumer.

  3. Create a new method search with the signature public void search()

  4. Implement the method with searchConsumer.accept(searchText)

  5. Annotate the method with @UIButton.

    1. The position should be higher than the position of @UITextField, e.g. 20.

    2. The caption should be set to "Start Search".

The finished implementation should look like this:
private Consumer<String> searchConsumer;

public SearchSectionPmo(Consumer<String> searchConsumer) {
    this.searchConsumer = searchConsumer;

@UIButton(position = 20, caption = "Start Search")
public void search() {
As a button do not store any values, the annotation is not added to a getter method, but a void method. Whenever the user clicks on the button, the method search() is invoked. The corresponding PMO property is called search.

The search action itself is a functionality that should not be directly implemented in the PMO as it requires interaction with external systems. Thus it should be passed to the PMO as a constructor argument.

In your application, you will often use the functional interfaces org.linkki.util.handler.Handler and Consumer<T> in order to separate a button’s execution logic from its definition within the Presentation Model Object (PMO) classes. The Handler is a representation of a method without arguments and without a return value.

Creating and showing SearchSectionPmo on a Page

Now that you have the search input section, follow the following steps to create and display the section represented by SearchSectionPmo:

  1. Create a Class SearchPage that extends AbstractPage.

  2. Create a field bindingManager of type BindingManager and initialize it with new DefaultBindingManager().

  3. Implement the method getBindingManager() which returns the field bindingManager.

  4. Define a new method public void search(String) with no implementation yet.

  5. Implement the method createContent with addSection(new SearchSectionPmo(this::search)).

The finished implementation should look like this:
public class SearchPage extends AbstractPage (1)
    private final BindingManager bindingManager = new DefaultBindingManager();

    public SearchPage(BusinessPartnerRepository partnerRepository) {
        this.partnerRepository = partnerRepository;

    protected BindingManager getBindingManager() {
        return bindingManager;

    public void createContent() {
        addSection(new SearchSectionPmo(this::search));

    public void search(String searchText) {
        // will be implemented later
1 AbstractPage is an implementation of Page by linkki in which sections can be conveniently created and added by providing the corresponding PMOs.

Next, implement the search functionality as follows:

  1. Create a new field foundPartners with the type List<BusinessPartner> to store the search results.

  2. Initialize the field foundPartners with new ArrayList<>().

  3. Extend the constructor of SearchPage with an additional argument of type org.linkki.samples.appsample.model.BusinessPartnerRepository. This type is provided by the dependency sample-model and implements the seach functionality.

  4. Store the constructor argument in a new field partnerRepository.

  5. Implement the method search: Execute the method findBusinessPartner from partnerRepository with the method argument and store the result in the field foundPartners.

The finished implementation should look like this:
private final BusinessPartnerRepository partnerRepository;
private final List<BusinessPartner> foundPartners = new ArrayList<>();

public void search(String searchText) {
    var searchResults = partnerRepository.findBusinessPartners(searchText);

Showing SearchPage in BusinessPartnerView

The SearchPage is still not displayed yet. To create and display the page, go to the class BusinessPartnerView and modify the class as followed:

  1. Extend the constructor with an argument of type BusinessPartnerRepository.

  2. Annotate the constructor with @Autowired.

  3. Create a new Headline using the constructor and the argument "Partner Search" and add it to the view with add.

  4. Create a new SearchPage using the constructor.

  5. Call the init() method of the created SearchPage to create the content of the page and add the created SearchPage to the view with add.

The finished implementation should look like this:
public BusinessPartnerView(BusinessPartnerRepository repository) {
    Headline headline = new Headline("Partner Search");
    SearchPage searchPage = new SearchPage(repository);

The method AbstractPage.init() takes care of creating the page after the constructor has been called.

Viewing the result in the application

If you run your application now, you should be able to see the SearchSectionPmo. The view begins with the title "Partner Search" and is followed by a search bar and a search button called "Start Search".

search bar
Figure 3. Search bar and button

For now, clicking the search button does not affect the UI. The next step extends the UI to display the search result.