Updating components in parent from events fired from child view

I have a small example where I’m trying to show/hide buttons in the MainLayout from some child views. My code is as follows:

public class MainLayout extends AppLayout {
    private Button pageOne;
    private Button pageTwo;

    public MainLayout() {
        pageOne = new Button("Page One", event -> getUI().ifPresent(ui -> ui.navigate(PageOne.class)));
        pageTwo = new Button("Page Two", event -> getUI().ifPresent(ui -> ui.navigate(PageTwo.class)));

        HorizontalLayout header = new HorizontalLayout(pageOne, pageTwo);
        addToNavbar(header);

        ComponentUtil.addListener(this, PageAttachEvent.class, this::toggleButton);
    }

    public void toggleButton(PageAttachEvent event) {
        switch(event.getSource().getClassName()) {
            case "page-one":
                pageOne.setVisible(false);
                pageTwo.setVisible(true);
                break;
            case "page-two": 
                pageOne.setVisible(true);
                pageTwo.setVisible(false);
                break;
            default:
                pageOne.setVisible(false);
                pageTwo.setVisible(true);
        }
    }
}
@Route(value = "page-one", layout = MainLayout.class) 
@RouteAlias(value = "", layout = MainLayout.class)
@PageTitle("Page One")
public class PageOne  extends VerticalLayout {
    public PageOne() {
        addClassName("page-one");
        H1 h1 = new H1("Page One");
        add(h1);
    }

    @Override
    protected void onAttach(AttachEvent attachEvent) {
        fireEvent(new PageAttachEvent(this));
    }
}

I see I’m firing the event, but my listener method never runs. I can actually get this working by using findAncestor(MainLayout.class).toggleButton("page-one"); but after reading https://stackoverflow.com/questions/72250254/accessing-layout-from-inside-vaadin-23-view it seems using events is the correct way to do it. The cookbook adds the listener from the child components, is that why?

Implementing “AfterNavigationObserver” on your main layout should be enough. In there you can check which view was opened and act accordingly. It’s theoretically possible with the event system as well, but a little bit to clunky for such simple case IMHO

You added the listener on the mainlayout component and fired the event on a different component, so the listener cannot be invoked.
In the cookbook example listener and event are both handled by the UI component.
Anyway, I think it will be easier to implement afternavigationobserver in the mainlayout class

Exactly as @quirky-zebra mentioned above :slightly_smiling_face:

Perfect, thank you both.