Skip to content

Trigger Annotations

Triggers let a component react to lifecycle events — page load, field value changes, completed actions, custom browser events, and more — by automatically invoking a named action without the user clicking a button.


Declares a single reactive trigger. Repeatable via the @Triggers container, so multiple triggers can be stacked on the same class or method.

@Repeatable(Triggers.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Trigger {
TriggerType type();
String actionId();
int timeoutMillis() default 0;
int times() default 1;
String condition() default "";
String calledActionId() default "";
String propertyName() default "";
String eventName() default "";
}
AttributeTypeDefaultDescription
typeTriggerTypeThe event that fires this trigger (required).
actionIdStringID of the action to execute when the trigger fires (required).
timeoutMillisint0Delay in milliseconds before the action is invoked.
timesint1How many times the trigger may fire. 0 means unlimited.
conditionString""Expression that must evaluate to true for the trigger to fire.
calledActionIdString""For OnSuccess / OnError — the preceding action whose completion fires this trigger.
propertyNameString""For OnValueChange — the field name to watch.
eventNameString""For OnCustomEvent — the name of the browser custom event to listen for.

ValueWhen it fires
OnLoadOnce when the component is first rendered.
OnSuccessAfter the action identified by calledActionId completes successfully.
OnErrorAfter the action identified by calledActionId fails.
OnValueChangeWhen the value of the field named propertyName changes.
OnCustomEventWhen a browser custom event named eventName is dispatched.
OnEnterWhen the user presses Enter inside an input field.

Container annotation that holds multiple @Trigger declarations on the same element. You do not normally need to write @Triggers by hand — the Java compiler generates it automatically when you stack two or more @Trigger annotations.

@Retention(RetentionPolicy.RUNTIME)
public @interface Triggers {
Trigger[] value();
}

When to use triggers vs. plain action methods

Section titled “When to use triggers vs. plain action methods”
SituationUse
The user clicks a button to invoke logicAn action method (@Button, @Toolbar, @Action, etc.)
Logic must run as soon as the page loads@Trigger(type = TriggerType.OnLoad, actionId = "...")
One field update must reload another field’s options@Trigger(type = TriggerType.OnValueChange, ...)
An action must chain automatically after another succeeds@Trigger(type = TriggerType.OnSuccess, calledActionId = "...")
A third-party widget fires a custom browser event@Trigger(type = TriggerType.OnCustomEvent, eventName = "...")

Triggers are declared on the class (not on the action method itself), which keeps reactive wiring at the top of the file and keeps action methods focused on logic.


The listing fires the built-in search action as soon as it renders, so the table is populated without the user pressing a search button.

Changes.java
@Title("Changes")
@Service
@Scope("prototype")
@Trigger(type = TriggerType.OnLoad, actionId = "search")
public class Changes extends Listing<NoFilters, ChangeRow> {
@Override
public ListingData<ChangeRow> search(
String searchText, NoFilters filters, Pageable pageable, HttpRequest httpRequest) {
// ...
}
}
@Trigger(
type = TriggerType.OnValueChange,
propertyName = "country",
actionId = "reloadRegions"
)
public class AddressForm {
String country;
String region;
Object reloadRegions() {
// reload region options based on the current value of `country`
return this;
}
}
@Trigger(
type = TriggerType.OnSuccess,
calledActionId = "save",
actionId = "sendConfirmation"
)
public class OrderForm {
@Toolbar
@Action(validationRequired = true)
void save() { /* persist */ }
void sendConfirmation() { /* send email */ }
}

Stack @Trigger annotations directly — the @Triggers container is implicit.

@Trigger(type = TriggerType.OnLoad, actionId = "loadDefaults")
@Trigger(type = TriggerType.OnValueChange, propertyName = "planType", actionId = "recalcPrice")
public class PricingForm {
String planType;
double price;
void loadDefaults() { planType = "standard"; }
Object recalcPrice() { price = pricingService.calc(planType); return this; }
}
@Trigger(type = TriggerType.OnLoad, actionId = "pollStatus", timeoutMillis = 3000, times = 0)
public class JobStatusPage {
String status;
Object pollStatus() {
status = jobService.getStatus(jobId);
return this;
}
}

times = 0 means the trigger fires indefinitely every timeoutMillis milliseconds until the user navigates away.