Application Framework

ApplicationLayout

When using linkki Application Framework, all views should be displayed in a customized ApplicationLayout which provides the application frame. Therefore, the first step to use application framework is to create a subclass of ApplicationLayout.

The main purpose of this class is to hold the custom implementation of ApplicationConfig in which the application frame is configured. This custom implementation can be passed to the super constructor. The implementation of ApplicationLayout itself should have a default constructor which makes it usable as a RouterLayout.

Example ApplicationLayout
public class PlaygroundAppLayout extends ApplicationLayout {

    private static final long serialVersionUID = -5604950024464910529L;

    public PlaygroundAppLayout() {
        super(new PlaygroundApplicationConfig());
    }

}

The ApplicationLayout surrounds the view with an ApplicationHeader and an optional ApplicationFooter.

The header is designed to have multiple menu actions on the left and may have some specific items like help menu or preferences on the right side.

The footer is hidden by default and can simply contain some application info.

Between ApplicationHeader and ApplicationFooter is the main area that displays the current view.

Application Configuration

The main aspects of the application are configured using the ApplicationConfig. This interface needs to be implemented once in every linkki application and provided to the ApplicationLayout.

ApplicationInfo

ApplicationConfig refers to an ApplicationInfo, which defines basic information about the application, such as name and copyright. Furthermore, parts of an ApplicationLayout are configured by the ApplicationConfig, such as the header, footer, as well as converters that should be used to convert values between UI and the underlying model.

Application Header

ApplicationConfig#getApplicationHeader configures the application header. An ApplicationHeader consists of two menu bars: one on the left, and one on the right.

The menu actions on the left are directly defined in the Application Configuration by the method getMenuItemDefinitions.

The menu bar on the right contains a help menu by default. In the help menu, a sub menu item displays the ApplicationInfo in a dialog. The right menu can be customized as follows:

  1. Create a custom subclass of ApplicationHeader

  2. In the subclass:

    • To customize items of the help menu, override addHelpMenuItems

      • To add submenu items for toggling between different theme variants, use addThemeVariantToggles(MenuItem, ThemeVariantToggleMenuItemDefinition…​)

    • To customize items of the menu bar, override createRightMenuBar

  3. Use the custom subclass in ApplicationConfig#getHeaderDefinition

The right menu and its items have the following IDs by default:

appmenu-right

the right menu bar

appmenu-help

the help menu within the right menu bar

appmenu-info

the info submenu within the help menu

appmenu-theme

the theme variant submenu within the help menu

As HTML IDs are very convenient for UI testing, custom items added to the menu should also set an ID.

The class ApplicationMenuItemDefinition can be used to create items for the right menu.

ThemeVariantToggleMenuItemDefinition

MenuItems for theme variants can be added using the method addThemeVariantToggles(MenuItem, ThemeVariantToggleMenuItemDefinition…​) for example by overriding ApplicationHeader#createRightMenuBar.

ApplicationHeader
    @Override
    protected MenuBar createRightMenuBar() {
        MenuBar rightMenuBar = super.createRightMenuBar();
        MenuItem settings = rightMenuBar.addItem(VaadinIcon.COG.create());
        settings.setId("appmenu-settings");
        addThemeVariantToggles(settings, ThemeVariantToggleMenuItemDefinition.LUMO_DARK,
                               ThemeVariantToggleMenuItemDefinition.LINKKI_CARD,
                               ThemeVariantToggleMenuItemDefinition.LINKKI_COMPACT);
        return rightMenuBar;
    }
ApplicationMenuItemDefinition

MenuItems can be added to the ApplicationHeader using ApplicationMenuItemDefinition.

For creation, ApplicationMenuItemDefinition provides following constructors:

  • ApplicationMenuItemDefinition(String, String, Handler): Name and ID of the menu and a Handler that is executed on click.

  • ApplicationMenuItemDefinition(String, String, List<ApplicationMenuItemDefinition>): Name and ID of the menu and a list of ApplicationMenuItemDefinitions as sub-menu items. Sub-menus are added in order as defined in the sub-menu list.

  • ApplicationMenuItemDefinition(String, String, String): Name and ID of the menu and a URL as String to navigate to on click. This method handles both external and internal links.

  • ApplicationMenuItemDefinition(String, String, Class<? extends Component>): Name and ID of the menu and a Class to navigate to. The given class should be a route component.

The ID of the menu item is used as ID of the HTML element in the DOM. This makes it easier to navigate through the menu in UI tests.
The old versions of these constructors without an parameter for the ID have been deprecated. If they are still used the ID of the menu is derived from the name and converted to an appropriate format with the prefix appmenu-.

More complex menu items can be implemented by creating menu item definitions directly instead of inheriting. If the implementation needs to be customizable, it can be easily done by composing ApplicationMenuItems.

The following example uses a factory class to create the menu. A menu item contains one sub-menu item with caption "New". If only one sub-sub-menu item is given, the sub-menu-item shows a notification on click (caption → New). If multiple sub-sub-menu items are given, those are shown in the sub-menu item (caption → New → sub-sub-menu items). This can be used in ApplicationConfig#getMenuItemDefinitions()

CustomMenuItemDefinitionCreator
public class CustomMenuItemDefinitionCreator {

    private CustomMenuItemDefinitionCreator() {
        throw new IllegalStateException("Utility class");
    }

    public static ApplicationMenuItemDefinition createMenuItem(String name,
            String id,
            List<MySubSubMenuItem> subSubMenuItems) {
        return new ApplicationMenuItemDefinition(name, id,
                Collections.singletonList(createSubMenuItem(subSubMenuItems)));
    }

    private static ApplicationMenuItemDefinition createSubMenuItem(List<MySubSubMenuItem> subSubMenuItems) {
        String subMenuName = "New";
        if (subSubMenuItems.size() > 1) {
            return new ApplicationMenuItemDefinition(subMenuName, "new",
                    subSubMenuItems.stream()
                            .map(i -> new ApplicationMenuItemDefinition(i.getName(), i.getId(),
                                    () -> i.getCommand().apply()))
                            .collect(Collectors.toList()));
        } else if (subSubMenuItems.size() == 1) {
            MySubSubMenuItem onlyItem = subSubMenuItems.get(0);
            return new ApplicationMenuItemDefinition(onlyItem.getName(), onlyItem.getId(), onlyItem.getCommand());
        } else {
            return new ApplicationMenuItemDefinition(subMenuName, "new", Handler.NOP_HANDLER);
        }
    }

    public static class MySubSubMenuItem {
        private String name;
        private String id;

        public MySubSubMenuItem(String name, String id) {
            this.name = name;
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public String getId() {
            return id;
        }

        public Handler getCommand() {
            return () -> Notification.show(name);
        }
    }
}

To add a MenuItem when to your own ApplicationMenu or SubMenu the following methods can be used:

  • createItem(ApplicationMenu): Creates and adds a MenuItem to the given ApplicationMenu.

  • createItem(SubMenu): Creates and adds a MenuItem to the given SubMenu.

ApplicationMenu
ApplicationMenuItemDefinition startMenuItemDefinition = new ApplicationMenuItemDefinition("Start", () -> goToStart());
ApplicationMenu menuBar = new ApplicationMenu();
startMenuItemDefinition.createItem(menuBar);
SubMenu
ApplicationMenuItemDefinition itemDefinition = new ApplicationMenuItemDefinition("Start", () -> goToStart());
-- new MenuBar to create item for sub-menu
MenuBar menuBar = new MenuBar();
SubMenu subMenu = menuBar.addItem("item").getSubMenu();
itemDefinition.createItem(subMenu);

Default variants

The method getDefaultVariants determines which variants of the linkki theme should be applied by default.

ErrorHandler

In case of an unhandled runtime exception, linkki shows information about the occurred error such as timestamp, a short description and a detailed stacktrace in a dialog. After confirming the dialog, the user is redirected to either the default main URL, or a custom error URL that can be set by extending the DialogErrorHandler and pass it to the super-constructor.

DialogErrorHandler
public class DialogErrorHandler implements ErrorHandler {
    public DialogErrorHandler(BiFunction<ErrorEvent, Handler, ConfirmationDialog> dialogCreator, String startView) {
        this.dialogCreator = dialogCreator;
        this.startView = startView;
    }

    @Override
    public void error(ErrorEvent event) {
        ConfirmationDialog dialog = dialogCreator.apply(errorEvent, () -> UI.getCurrent().navigate(startView, QueryParameters.fromString("errorOccurred")));
        dialog.open();
    }
}

In addition, ApplicationLayout#getErrorHandler() must be overridden to return your custom ErrorHandler.

When redirecting, an additional errorOccurred query parameter is added to the URL. This parameter enables the target view to react to the error. It can be useful in some cases to enable or disable certain features in BeforeLeaveHandler#beforeLeave(BeforeLeaveEvent) in case of an error. For more information refer to Vaadin’s BeforeLeaveEvent