Step 3: Implementing a new View with LinkkiTabLayout

This step introduces you to the navigation between views using Vaadin. You will also learn how to use the layout type LinkkiTabLayout.

To display more detailed information of a business partner, you will create a new view that will display two pages as tabs: BasicDataPage and AddressPage. Each page will correspond to a tab sheet. For now, the pages will stay empty.

detail empty
Figure 1. BasicDataPage

Creating View to display partner details

If you look at the implementation of BusinessPartnerView, you will notice the @Route annotation. This is a Vaadin annotation and it is required for navigating between views. Note that the value attribute represents the URL. If value is "example", you can access this view using the URL "localhost:8080/example".

Create the new view by creating a class called PartnerDetailsView, annotating it with @Route and doing the following steps:

  1. Set the attribute value to "PartnerDetails".

  2. As with BusinessPartnerView, use the layout BusinessPartnerLayout, otherwise you will not have the navigation bar at the top of the view.

  3. Extend PartnerDetailsView to Div, otherwise you will get an error when accessing this view.

The finished implementation should look like this:

PartnerDetailsView.java
@Route(value = "PartnerDetails", layout = BusinessPartnerLayout.class)
public class PartnerDetailsView extends Div {

}

Transforming PartnerDetailsView to a LinkkiTabLayout

Overall, PartnerDetailsView should show a tab layout with the two pages as tab sheets. To do this:

  1. Use LinkkiTabLayout as the super class of PartnerDetailsView instead of Div.

  2. To have the tabs on the left of the view, use the vertical orientation of LinkkiTabLayout by calling the super constructor with Orientation.VERTICAL as an argument.

  3. If you want, you can change the background of the tab bar by adding THEME_VARIANT_SOLID to the list of the view themes in the constructor with this.getElement().getThemeList().add(THEME_VARIANT_SOLID);.

PartnerDetailsView.java
public class PartnerDetailsView extends LinkkiTabLayout {

    public PartnerDetailsView() {
        super(Orientation.VERTICAL);
        this.getElement().getThemeList().add(THEME_VARIANT_SOLID);
    }
}

You can now run your application and access your new view via "localhost:8080/PartnerDetails".

Accessing PartnerDetailsView for a given partner

The URL currently doesn’t allow you to show the information of a specific partner. Therefore, you need to add a parameter to the URL.

One way to achieve this is to have the view implement the interface HasUrlParameter<String> and override the method setParameter(BeforeEvent event, @OptionalParameter String parameter). You will implement this method later.

PartnerDetailsView.java
public class PartnerDetailsView extends LinkkiTabLayout implements HasUrlParameter<String> {

    @Override
    public void setParameter(BeforeEvent event, @OptionalParameter String parameter) {
        // will be implemented later
    }
}

You are now ready to add the URL to the UILink that you implemented in the previous step.

Go back to the SearchResultRowPmo and let the method getDetails return the URL of PartnerDetailsView, which is "PartnerDetails/" + partner.getUuid().toString(), with the parameter being the UUID of the partner.

SearchResultRowPmo.java
@UILink(position = 30, caption = "Show Details")
public String getDetails() {
    return "PartnerDetails/" + partner.getUuid().toString();
}

If you run your application now, you should be able to click on the "Show Details" link and navigate to the empty PartnerDetailsView.

If you take a look at the URL, you should be able to see the ID of a partner. For John Doe, the parameter is ac54f2f9-7b82-4bc8-ab69-554cac5926f1.
search result table link
Figure 2. BusinessPartnerView

Retrieving the correct BusinessPartner

Now that the "ID" of a BusinessPartner is passed to the URL of the PartnerDetailsView, you need to retrieve it, otherwise you won’t know which partner you want to display information for.

To retrieve the correct BusinessPartner, do the following:

  1. Create a field partnerRepository of type BusinessPartnerRepository.

  2. Create a field currentPartner of type Optional<BusinessPartner>.

  3. Pass a BusinessPartnerRepository to the constructor and annotate the constructor with @Autowired, as you did for BusinessPartnerView.

  4. Assign the constructor argument to the field partnerRepositiory.

  5. Initialize the field currentPartner with Optional.empty().

  6. In the method setParameter, assign Optional.ofNullable(parameter).map(UUID::fromString).map(partnerRepository::getBusinessPartner) to the field currentPartner.

The finished implementation should look like this:

PartnerDetailsView.java
private final BusinessPartnerRepository partnerRepository;
private Optional<BusinessPartner> currentPartner;

@Autowired
public PartnerDetailsView(BusinessPartnerRepository partnerRepository) {
    ...
    this.partnerRepository = partnerRepository;
    this.currentPartner = Optional.empty();
}

@Override
public void setParameter(BeforeEvent event, @OptionalParameter String parameter) {
    currentPartner = Optional.ofNullable(parameter).map(UUID::fromString)
            .map(partnerRepository::getBusinessPartner);
}

Adding tab sheets to PartnerDetailsView

Now that you have the correct BusinessPartner, you can add the two tab sheets that will display detailed information about the partner and their addresses. However, at the moment you can access the view whether the UUID of a BusinessPartner is correct or not.

Therefore, you will an additional tab sheet to handle error cases: If the UUID is missing or incorrect, the first tab will be shown and the other two will be hidden. Otherwise, the first tab will be hidden and the other two will display detailed information on the BusinessPartner.

Before adding the tab sheets, you need to create the component the tab sheets will display. Do the following:

  1. Create a method createErrorLayout with the signature Component createErrorLayout().

  2. Let the method return new Div(new Text("No partner ID was provided or no partner could be found with the given ID")).

  3. Create a method createBasicDataPage with the signature Component createBasicDataPage(BusinessPartner partner).

  4. Let the method return new Div(new Text("Basic Data Page for partner " + partner.getUuid())).

  5. Create a method createAddressPage with the signature Component createAddressPage(BusinessPartner partner).

  6. Let the method return new Div(new Text("Address Page for partner " + partner.getUuid())).

The finished implementation should look like this:

PartnerDetailsView.java
private Component createErrorLayout() {
    return new Div(new Text("No partner ID was provided or no partner could be found with the given ID"));
}

private Component createBasicDataPage(BusinessPartner partner) {
    return new Div(new Text("Basic Data Page for partner " + partner.getUuid()))
}

private Component createAddressPage(BusinessPartner partner) {
    return new Div(new Text("Address Page for partner " + partner.getUuid()))
}

To add the tab sheets, use the method addTabSheets. The method takes a list of LinkkiTabSheet as the argument. Create the three LinkkiTabSheet as follows:

  1. Use a builder with LinkkiTabSheet.builder(), which takes the ID of the tab sheet as a String argument:

    1. Use "error" as the ID for the first tab sheet.

    2. Use "basic-data" as the ID for the second tab sheet.

    3. Use "address" as the ID for the third tab sheet.

  2. Add a caption to the tab sheet using .caption() which will be displayed on the tab element. In this case, you will display icons:

    1. Use VaadinIcon.WARNING.create() as the caption for the first tab sheet.

    2. Use VaadinIcon.USER.create() as the caption for the second tab sheet.

    3. Use VaadinIcon.HOME.create() as the caption for the third tab sheet.

  3. Set the content of the tab sheet using .content(), which takes a Supplier<Component> as the argument, where Component is a Vaadin Component:

    1. Use this::createErrorLayout as the argument for the first tab sheet.

    2. Use this::createBasicDataPage as the argument for the second tab sheet.

    3. Use this::createAddressPage as the argument for the third tab sheet.

  4. Set the visibilty of the tab sheet using .visibleWhen():

    1. Use () → currentPartner.isEmpty() as the argument for the first tab sheet.

    2. Use () → currentPartner.isPresent() as the argument for the second tab sheet.

    3. Use () → currentPartner.isPresent() as the argument for the third tab sheet.

  5. Build the tab sheet with .build().

The finished implementation should look like this:

PartnerDetailsView.java
public PartnerDetailsView(BusinessPartnerRepository partnerRepository) {
    ...
    addTabSheets(
            LinkkiTabSheet.builder("error").caption(VaadinIcon.WARNING.create()).content(this::createErrorLayout)
                    .visibleWhen(() -> currentPartner.isEmpty()).build(),
            LinkkiTabSheet.builder("basic-data").caption(VaadinIcon.USER.create())
                    .content(() -> currentPartner.map(this::createBasicDataPage).orElseGet(Div::new))
                    .visibleWhen(() -> currentPartner.isPresent()).build(),
            LinkkiTabSheet.builder("address").caption(VaadinIcon.HOME.create())
                    .content(() -> currentPartner.map(this::createAddressPage).orElseGet(Div::new))
                    .visibleWhen(() -> currentPartner.isPresent()).build());
}

If you run your application, you should be able to access the new view from BusinessPartnerView and navigate to the two empty pages via the tabs on the left.

The first tab should have a user icon and display for e.g. John Doe "Basic Data Page for partner ac54f2f9-7b82-4bc8-ab69-554cac5926f1".

detail empty
Figure 3. BasicDataPage

The second tab should have a home icon and display for e.g. John Doe "Address Page for partner ac54f2f9-7b82-4bc8-ab69-554cac5926f1".

address empty
Figure 4. AddressPage

If you remove the UUID from the URL, you should get only one tab. This tab should have a warning icon and display "No partner ID was provided or no partner could be found with the given ID".

error
Figure 5. Error

The next step extends the UI by adding a menu item in order to navigate back to BusinessPartnerView.