Step 2: Displaying the search result in a table

To display the search results, matching business partners should be shown in a table below the search bar. The user should only be able to gather brief information about the business partner for now.

Figure 1. Search result table


To build a table in linkki you need two PMO classes. The first one is a row PMO, i.e. it defines the structure of one row of the table. We call it SearchResultRowPmo. The second one is a ContainerPmo, it builds the table out of the rows. We call it SearchResultsTablePmo. To get a better understanding about tables in linkki have a look at the Tables section in the UI Components chapter of the linkki Documentation.

Every row consists of a text field, a text area, and a button. The text field shall display the name of a business partner and the text area the first address of their address list. The button leads us to the PartnerDetailView, which we are going to implement in Step 3.

Again, we do not implement the method being triggered by the button in the SearchResultRowPmo. The difference between this case and the one in the SearchSectionPmo is that this time we use a Consumer<T> instead of a Handler. That is because we have to pass the business partner whose details we want as an argument to our method.

If you want, you can also configure the width of the columns in your table. This can be done by using the annotation @UITableColumns above the UI elements.
    private BusinessPartner partner;
    private Consumer<BusinessPartner> consumer;
    private String firstAddress;

    public SearchResultRowPmo(
            BusinessPartner partner, Consumer<BusinessPartner> consumer) {
        this.partner = partner;
        this.consumer = consumer;
        this.firstAddress = partner.getFirstAddress();

    public BusinessPartner getPartner() {
        return partner;

    @UITableColumn(expandRatio = 2)
    @UIButton(position = 30, caption = "Show Details", showIcon = true,
            icon = VaadinIcons.PLUS)
    public void detailsButton() {

The SearchResultsTablePmo receives the annotation @UISection with the caption "Search Results" and has to extend the abstract class SimpleTablePmo which takes our model object BusinessPartner and the SearchResultRowPmo as type arguments. We implement the required methods by creating the constructor and a new object of SearchResultRowPmo in the createRow method.
@UISection(caption = "Search Results")
public class SearchResultsTablePmo
        extends SimpleTablePmo<BusinessPartner, SearchResultRowPmo> {

    private Consumer<BusinessPartner> consumer;

    public SearchResultsTablePmo(
            Supplier<List<? extends BusinessPartner>> modelObjectsSupplier,
            Consumer<BusinessPartner> showDetailsConsumer) {
        this.consumer = showDetailsConsumer;

    protected SearchResultRowPmo createRow(BusinessPartner partner) {
        return new SearchResultRowPmo(partner, consumer);


Add table to SearchPage

We now complete the SearchPage. Adding the section to the SearchPage is done as before. The consumer for the SearchResultsTablePmo will be defined in the constructor of the MainView. Similar to the layout definition in the MainView, we set the expand ratio of the SearchSectionPmo to 0 in order to use only the required space for it. The expand ratio of the SearchResultsTablePmo is set to 1, to fill the remaining space. To make sure that the SearchResultsTablePmo receives the rest of the page and does not cross its borders, we set it to full size.
    public void createContent() {
        AbstractSection searchSection =
                addSection(new SearchSectionPmo(this::search));
        setExpandRatio(searchSection, 0);

        SearchResultsTablePmo searchResultsTablePmo =
                new SearchResultsTablePmo(
                        () -> displayedPartners, showDetailsConsumer);
        AbstractSection resultSection = addSection(searchResultsTablePmo);

        setExpandRatio(resultSection, 1);

Now we switch to the MainView and define the consumer.
    public MainView(BusinessPartnerRepository repository) {
        Consumer<BusinessPartner> showDetailsConsumer =
        SearchPage searchPage =
                new SearchPage(repository, showDetailsConsumer);

    public void showPartnerDetails(BusinessPartner partner) {

We implement the method showPartnerDetails and leave it empty for now. This method will be responsible for the navigation to the PartnerDetailView that will be implemented in the next step.

If we start the application now, we can search for an arbitrary string and see all the matching business partners in the result table below the search bar. For example searching for "doe" shows us almost all the results that our repository contains.