Metadata supplier interfaces
These lightweight interfaces let you provide page metadata (title, subtitle, browser title) dynamically at runtime instead of hardcoding them with annotations.
TitleSupplier
Section titled “TitleSupplier”Provides the page title dynamically.
public interface TitleSupplier { String title();}@Route("/customers/{id}")public class CustomerDetailPage implements ComponentTreeSupplier, TitleSupplier {
private final CustomerRepository repo;
@Override public Component component(HttpRequest httpRequest) { var id = httpRequest.lastPathItem(); return Form.builder().title(title()).build(); }
@Override public String title() { return "Customer detail"; }}SubtitleSupplier
Section titled “SubtitleSupplier”Provides the page subtitle dynamically.
public interface SubtitleSupplier { String subtitle();}@Route("/report")public class ReportPage implements ComponentTreeSupplier, SubtitleSupplier {
@Override public Component component(HttpRequest httpRequest) { return Form.builder().title("Report").subtitle(subtitle()).build(); }
@Override public String subtitle() { return "Generated on " + LocalDate.now(); }}ValidationSupplier
Section titled “ValidationSupplier”Provides cross-field validations programmatically — an alternative to the @Validation annotation for dynamic validation logic.
public interface ValidationSupplier { List<Validation> validations();}public class BookingForm implements ValidationSupplier { LocalDate startDate; LocalDate endDate;
@Override public List<Validation> validations() { return List.of( new Validation("startDate > endDate", "endDate", "End date must be after start date") ); }}RuleSupplier
Section titled “RuleSupplier”Provides conditional UI rules programmatically — an alternative to the @Rule annotation.
public interface RuleSupplier { List<Rule> rules();}public class AccountForm implements RuleSupplier { String accountType; String vatNumber;
@Override public List<Rule> rules() { return List.of( Rule.builder() .filter("accountType == 'BUSINESS'") .action(RuleAction.set) .fieldName("vatNumber") .fieldAttribute(RuleFieldAttribute.visible) .value("true") .build() ); }}DataSupplier
Section titled “DataSupplier”Provides arbitrary data to the component on each render cycle.
public interface DataSupplier { Object data(HttpRequest httpRequest);}@Route("/summary")public class SummaryPage implements ComponentTreeSupplier, DataSupplier {
@Override public Component component(HttpRequest httpRequest) { return Form.builder().title("Summary").build(); }
@Override public Object data(HttpRequest httpRequest) { return Map.of("totalRevenue", revenueService.getTotal()); }}CommandSupplier
Section titled “CommandSupplier”Provides a list of UICommand objects that the client executes after rendering.
public interface CommandSupplier { List<UICommand> commands(HttpRequest httpRequest);}@Route("/setup")public class SetupPage implements ComponentTreeSupplier, CommandSupplier {
@Override public Component component(HttpRequest httpRequest) { return new Text("Setting up..."); }
@Override public List<UICommand> commands(HttpRequest httpRequest) { return List.of(new UICommand("navigate", "/dashboard")); }}PostHydrationHandler
Section titled “PostHydrationHandler”Called by Mateu after it re-hydrates the component state from the client. Use it to perform side effects or load data after the state has been populated.
public interface PostHydrationHandler { void onHydrated(HttpRequest httpRequest);}public class OrderForm implements PostHydrationHandler { String orderId; String status;
@Override public void onHydrated(HttpRequest httpRequest) { // called after orderId/status have been populated from the client state auditService.log("Order form loaded: " + orderId); }}