UI.getCurrent().getChildren().findFirst().get() returns null on view refresh

I make a call in a view’s constructor called attachButtonsToMainLayout() which looks like this:

private void attachButtonsToMainLayout() {
if (UI.getCurrent().getChildren().findFirst().isPresent()) {
MainLayout mainLayout = (MainLayout) UI.getCurrent().getChildren().findFirst().get();
mainLayout.addToNavbar(getButtonLayout());
mainLayout.addToNavbar(getSearchLayout());
}
}

When I click on the link in the SideNav for the route associated with the view, the MainLayout is found and I can add the buttons to the top nav bar with no problem. However, when I refresh the route with the browser, the second call to UI.getCurrent().getChildren().findFirst() returns a null for the MainLayout.

Is there another way I can achieve this so that my buttons that I create in my view class can be attached to the NavBar in my MainLayout?

Any help on this would be greatly appreciated! Thanks and take care!

  • Clint

Just an idea: stop using the constructor :slightly_smiling_face:

I’m now listening for AttachEvent and DetachEvent and adding my buttons in an onAttach method and removing them with an onDetach method and all is well now!

I’m still relatively new to Vaadin, but starting to get the hang of things!

The reason for this is that the component instances can be attached to the UI only after they have been created. From this follows that components will not be attached when their constructors run. The exact ordering between creating instances and attaching them depends on various circumstances so it’s not safe to make any assumptions there.

For your use case, I would recommend defining the main layout as a UI-scoped bean and injecting it into any component that needs access to it. In that way, you avoid having an implicit dependency on the component hierarchy.

Also never do something like this. This implicits too much knowledge of the concrete dom structure and will break as soon as you change some minor thing.

If you search for child components, try to be as abstract and general as possible, to make your code more robust, for instance

Optional<MainLayout> o = UI.getCurrent().getChildren().filter(MainLayout.class::isInstance).map(MainLayout.class::cast);

// consume the optional, for instance via orElseThrow() or ifPresent() or ifPresentOrElse ...