3.5.4.3. BaseAction

BaseAction is a base class for actions implementation. It is recommended to derive custom actions from it when declarative actions creation functionality is insufficient.

When creating a custom action class, you should implement actionPerform() method and pass action identifier to the BaseAction constructor. You can override any property getters: getCaption(), getDescription(), getIcon(), getShortcut(), isEnabled(), isVisible(), isPrimary(). Standard implementations of these methods return values set by setter methods, except the getCaption() method. If the action name is not explicitly set by setCaption() method, it retrieves message using action identifier as key from the the localized message pack corresponding to the action class package. If there is no message with such key, then the key itself, i.e. the action identifier, is returned.

Alternatively, you can use the fluent API for setting properties and providing a lambda expression for handling the action: see withXYZ() methods.

BaseAction can change its enabled and visible properties depending on user permissions and current context.

BaseAction is visible if the following conditions are met:

  • setVisible(false) method was not called;

  • there is no hide UI permission for this action.

The action is enabled if the following conditions are met:

  • setEnabled(false) method was not called;

  • there are no hide or read-only UI permissions for this action;

  • isPermitted() method returns true;

  • isApplicable() method returns true.

Usage examples:

  • Button action:

    @Inject
    private Notifications notifications;
    @Inject
    private Button helloBtn;
    
    @Subscribe
    protected void onInit(InitEvent event) {
        helloBtn.setAction(new BaseAction("hello") {
            @Override
            public boolean isPrimary() {
                return true;
            }
    
            @Override
            public void actionPerform(Component component) {
                notifications.create()
                        .withCaption("Hello!")
                        .withType(Notifications.NotificationType.TRAY)
                        .show();
            }
        });
        // OR
        helloBtn.setAction(new BaseAction("hello")
                .withPrimary(true)
                .withHandler(e ->
                        notifications.create()
                                .withCaption("Hello!")
                                .withType(Notifications.NotificationType.TRAY)
                                .show()));
    }

    In this example, the helloBtn button caption will be set to the string located in the message pack with the hello key. You can override the getCaption() action method to initialize button name in a different way.

  • Action of a programmatically created PickerField:

    @Inject
    private UiComponents uiComponents;
    @Inject
    private Notifications notifications;
    @Inject
    private MessageBundle messageBundle;
    @Inject
    private HBoxLayout box;
    
    @Subscribe
    protected void onInit(InitEvent event) {
        PickerField pickerField = uiComponents.create(PickerField.NAME);
    
        pickerField.addAction(new BaseAction("hello") {
            @Override
            public String getCaption() {
                return null;
            }
    
            @Override
            public String getDescription() {
                return messageBundle.getMessage("helloDescription");
            }
    
            @Override
            public String getIcon() {
                return "icons/hello.png";
            }
    
            @Override
            public void actionPerform(Component component) {
                notifications.create()
                        .withCaption("Hello!")
                        .withType(Notifications.NotificationType.TRAY)
                        .show();
            }
        });
        // OR
        pickerField.addAction(new BaseAction("hello")
                .withCaption(null)
                .withDescription(messageBundle.getMessage("helloDescription"))
                .withIcon("icons/ok.png")
                .withHandler(e ->
                        notifications.create()
                                .withCaption("Hello!")
                                .withType(Notifications.NotificationType.TRAY)
                                .show()));
        box.add(pickerField);
    }

    In this example an anonymous BaseAction derived class is used to set the action of the picker field button. The button caption is not displayed, as an icon with a description, which pops up when hovering mouse cursor, is used instead.

  • Table action:

    @Inject
    private Notifications notifications;
    @Inject
    private Table<Customer> table;
    @Inject
    private Security security;
    
    @Subscribe
    protected void onInit(InitEvent event) {
        table.addAction(new HelloAction());
    }
    
    private class HelloAction extends BaseAction {
    
        public HelloAction() {
            super("hello");
        }
    
        @Override
        public void actionPerform(Component component) {
            notifications.create()
                    .withCaption("Hello " + table.getSingleSelected())
                    .withType(Notifications.NotificationType.TRAY)
                    .show();
        }
    
        @Override
        protected boolean isPermitted() {
            return security.isSpecificPermitted("myapp.allow-greeting");
        }
    
        @Override
        public boolean isApplicable() {
            return table != null && table.getSelected().size() == 1;
        }
    }

    In this example, the HelloAction class is declared, and its instance is added to the table’s actions list. The action is enabled for users who have myapp.allow-greeting security permission and only when a single table row is selected. The latter is possible because BaseAction’s target property is automatically assigned to the action when it is added to a ListComponent descendant (Table or Tree).

  • If you need an action, which becomes enabled when one or more table rows are selected, use BaseAction’s descendant - ItemTrackingAction, which adds default implementation of isApplicable() method:

    @Inject
    private Table table;
    @Inject
    private Notifications notifications;
    
    @Subscribe
    protected void onInit(InitEvent event) {
        table.addAction(new ItemTrackingAction("hello") {
            @Override
            public void actionPerform(Component component) {
                notifications.create()
                        .withCaption("Hello " + table.getSelected().iterator().next())
                        .withType(Notifications.NotificationType.TRAY)
                        .show();
            }
        });
    }