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

searchbar
Figure 1. Search bar and button

Creating the section to enter search criteria

Our first browser window should display a search field and a result table. These segments will be implemented as sections. In linkki, every section is represented by a Presentation Model Object (PMO) which is annotated with @UISection. If you want more detailed information about sections in linkki, you can have a look at the chapter Creation of a UI with linkki of the linkki documentation.

We call our first section SearchSectionPmo. It will contain a text field representing the search field and a button to trigger the search for the entered string. For a further explanation of the implementation of text fields, buttons and more UI elements you can have a look at the sections Layout of Sections and UI Element in the chapter "UI Components" of the linkki documentation. The implementation looks like this:

SearchSectionPmo.java
@UISection
public class SearchSectionPmo {

    public static final String PROPERTY_SEARCHTEXT = "searchText";

    private String searchText = "";

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

    public void setSearchText(String searchText) {
        this.searchText = searchText;
    }
}

The UI element UITextField is declared via the annotation @UITextField. The annotation is added to the method getSearchText as the content of the textfield should be determined by the return value of the method. We say that UI element UITextField is bound to the so called PMO property searchText.

A PMO Property can have a field, a getter and a setter, but only the getter is mandatory. Once the section is created from the PMO, linkki will bind the value of the text field widget to the getSearchText and setSearchText method of the PMO. That means whenever the user input changes, the setSearchText method will be called, including the actual user 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:

databinding
Figure 2. Data binding

In addition to the input field, there should be a button to trigger the search. For this purpose, we define a method void searchButton() that is annotated with the annotation @UIButton. The search action itself is a functionality that should not be directly implemented in the PMO as it requires interaction with external systems. Thus, we will use a Consumer to represent the search action which is passed to the constructor.

SearchSectionPmo.java
    private Consumer<String> searchConsumer;

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

    @UIButton(position = 20, showIcon = true, icon = VaadinIcons.SEARCH,
            captionType = CaptionType.NONE, shortcutKeyCode = KeyCode.ENTER,
            styleNames = { ValoTheme.BUTTON_PRIMARY })
    public void searchButton() {
        searchConsumer.accept(getSearchText());
    }

The button is now bound to the PMO Property searchButton. Whenever the user clicks on the button, the method searchButton() is invoked.

Our search button should be the primary action on the page, i.e. the button should also be triggered when pressing the enter key. This can be done by using the annotation property shortcutKeyCode in @UIButton.

In our application, we will often use the functional interfaces 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. The Consumer<T> is a representation of a method with one argument and without a return value. In both cases, the interfaces are often implemented using lambda expressions. While the Consumer<T> interface is part of the standard Java package java.util.function, the Handler interface is part of the linkki package org.linkki.util.handler.

Create and show SearchSectionPmo on a Page

Now that we have the search text input section, we can create a page to display it.

For our SearchPage, we use AbstractPage as a super class. AbstractPage is an implementation of Page by linkki in which sections can be conveniently created and added by providing the corresponding PMOs.

The SearchPage needs to override two abstract methods: createContent and getBindingManager. First, we construct a DefaultBindingManager as a new member variable and have the getBindingManager method return it. For further information regarding the BindingManager you can have a look at the section BindingContext Basics in the chapter "Architecture" of the linkki Documentation.

SearchPage.java
    private BindingManager bindingManager = new DefaultBindingManager(
            ValidationService.NOP_VALIDATION_SERVICE);

    @Override
    protected BindingManager getBindingManager() {
        return bindingManager;
    }

We want to add our SearchSectionPmo in the createContent method. However, we have to implement the search beforehand.

We define a new method void search(String). It should call the findBusinessPartner method from a BusinessPartnerRepository to receive the search results matching the input string. These results are then added to a List of displayedPartners. The repository has to be passed to the SearchPage in its constructor.

SearchPage.java
    private BusinessPartnerRepository repository;
    private List<BusinessPartner> displayedPartners = new ArrayList<>();

    public SearchPage(BusinessPartnerRepository repository) {
        this.repository = repository;
    }

Finally, we simply call addSection in createContent to add a section with a new instance of the search text section PMO. The search method will be used as the consumer for the search button. In our code, we use a method reference within the constructor.

Later in this tutorial, we will display the search results beneath this section, which should take up the remaining space on the page. However, two sections in a full size page would be evenly spaced by default. To prevent this, we set the expand ratio of the search section to 0 so it only takes up as much space as necessary.

SearchPage.java
    @Override
    public void createContent() {
        AbstractSection searchSection =
                addSection(new SearchSectionPmo(this::search));
        setExpandRatio(searchSection, 0);
    }

Show SearchPage in MainView

We will switch to the MainView and write a constructor for it. We annotate it with @Autowired. If you want a detailed explanation of the annotation @Autowired refer to the Using @Autowired chapter of the Spring Framework Documentation.

In the constructor of the MainView, we create a Headline and a SearchPage and add them to the MainView. In order to define the layout, we set the MainView to full size. Since the headline should not get more space than required, we set its expand ratio to zero. The remaining space should belong to the SearchPage, that’s why the value of its expand ratio is chosen greater than zero (in our case one). The enter method remains empty throughout the whole tutorial.

MainView.java
    @Autowired
    public MainView(BusinessPartnerRepository repository) {
        Headline headline = new Headline("Partner Search");
        addComponent(headline);
        SearchPage searchPage = new SearchPage(repository);
        searchPage.createContent();
        addComponent(searchPage);

        setSizeFull();
        setExpandRatio(headline, 0);
        setExpandRatio(searchPage, 1);
        searchPage.setSizeFull();

    }

If we run our application now, we are able to see the SearchSectionPmo with its search field and button.

For now, clicking the search button does not affect the UI.