Tutorial
Step 1: Implementing the search bar
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.
Creating the section to enter search criteria
In linkki, each layout element is represented 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:
-
Create a new field
searchText
that stores the user’s input. -
Create and implement the getter method for the property
searchText
with the signatureString getSearchText()
. -
Annotate the getter method with
@UITextField
. The annotation requires a valueposition
which can set to an arbitrary positive integer value, e.g. 10. -
Create and implement the setter method for the property
searchText
. This method should have the signaturepublic void setSearchText(String)
.
The finished implementation looks like this:
@UISection
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 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 The following image illustrates the data binding for the property Figure 2. Data binding
|
-
Create a new field
searchConsumer
of typeConsumer<String>
. -
Create a constructor with the signature
SearchSectionPmo(Consumer<String>)
which assigns the argument to the fieldsearchConsumer
. -
Create a new method
search
with the signaturepublic void search()
-
Implement the method with
searchConsumer.accept(searchText)
-
Annotate the method with
@UIButton
.-
The
position
should be higher than the position of@UITextField
, e.g. 20. -
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() {
searchConsumer.accept(getSearchText());
}
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
:
-
Create a Class
SearchPage
that extendsAbstractPage
. -
Create a field
bindingManager
of type BindingManager and initialize it withnew DefaultBindingManager()
. -
Implement the method
getBindingManager()
which returns the fieldbindingManager
. -
Define a new method
public void search(String)
with no implementation yet. -
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;
}
@Override
protected BindingManager getBindingManager() {
return bindingManager;
}
@Override
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:
-
Create a new field
foundPartners
with the typeList<BusinessPartner>
to store the search results. -
Initialize the field
foundPartners
withnew ArrayList<>()
. -
Extend the constructor of
SearchPage
with an additional argument of typeorg.linkki.samples.appsample.model.BusinessPartnerRepository
. This type is provided by the dependencysample-model
and implements the search functionality. -
Store the constructor argument in a new field
partnerRepository
. -
Implement the method
search
: Execute the methodfindBusinessPartner
frompartnerRepository
with the method argument and store the result in the fieldfoundPartners
.
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);
foundPartners.clear();
foundPartners.addAll(searchResults);
}
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:
-
Extend the constructor with an argument of type
BusinessPartnerRepository
. -
Annotate the constructor with
@Autowired
. -
Create a new
Headline
using the constructor and the argument"Partner Search"
and add it to the view withadd
. -
Create a new
SearchPage
using the constructor. -
Call the
init()
method of the createdSearchPage
to create the content of the page and add the createdSearchPage
to the view withadd
.
The finished implementation should look like this:
@Autowired
public BusinessPartnerView(BusinessPartnerRepository repository) {
Headline headline = new Headline("Partner Search");
add(headline);
SearchPage searchPage = new SearchPage(repository);
searchPage.init();
add(searchPage);
}
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".
For now, clicking the search button does not affect the UI. The next step extends the UI to display the search result.