Vaadin 14 accessing components of MainView

Hello, I have the following MainView:

public class MainView extends VerticalLayout implements PageConfigurator, BeforeEnterObserver, RouterLayout {

Inside this MainView I have the childWrapper where will be rendered the next pages.

On one of these pages I need to access a verticalLayout from MainView.

Does anyone know how to access MainView components through a view that uses it as a layout?

Hi,
You can passing MainView VerticalLayout as arguments when you create Child View.

Regards,
Reja

reja -:
Hi,
You can passing MainView VerticalLayout as arguments when you create Child View.

Regards,
Reja

Hi Reja.

How are you doing this argument passage?

In my case, I have these 2 events in the Main class that I can interact with:

@Override
public void beforeEnter(BeforeEnterEvent event) {
	System.out.println(" ------------------- beforeEnter()");
}

@Override
public void showRouterLayoutContent(HasElement content){
	System.out.println(" ------------------- showRouterLayoutContent()");
	content.getElement().getStyle().set("width", "100%");
	content.getElement().getStyle().set("height", "100%");
	childWrapper.getElement().appendChild(content.getElement());
}

Do you know another way?

Thanks.

Does anyone else have any other options or know how to pass as argument through a RouterLayout ?

Here’s an example of getting the layout. Mine is called AdminAppLayout, but replace it with MainView and it should work for you.

   /**
    * Returns the current AppLayout
    * 
    * @return
    */
   public static AdminAppLayout getInstance() {
      return (AdminAppLayout) UI.getCurrent().getChildren()
            .filter(component -> component.getClass() == AdminAppLayout.class).findFirst().orElse(null);
   }

Martin Israelsen:
Here’s an example of getting the layout. Mine is called AdminAppLayout, but replace it with MainView and it should work for you.

   /**
    * Returns the current AppLayout
    * 
    * @return
    */
   public static AdminAppLayout getInstance() {
      return (AdminAppLayout) UI.getCurrent().getChildren()
            .filter(component -> component.getClass() == AdminAppLayout.class).findFirst().orElse(null);
   }

Thanks, it worked for me.

You can always walk through the hierarchy using a way shown above (starting from UI instance) or other way around: asking a parent and walking from bottom to top and then find a required ascendant. Then you may ask children of it.

But I would say this is not a proper way from design point of view.
It’s better to decouple the components and avoid the knowledge of each other.
I would better use some event bus: when you need to communicate between components one component should send an event and another component should listen for events and receive events.

Thanks Denis Anisimov.

Really an event bus would be better.

I will try to implement it.

I have made a simple UI-scoped @Component with wich I am able to get my MainView instance without walking through any elements hierarchy. I’m not sure if this classifies as event bus, but it gets the job done and could be expanded for events.

The trick is to inject this component both in the MainView, as well as in each child view. In the mainView you then only have to set the current MainView of the “Bus”.

@Component
@UIScope
public class MainViewBus {
    private MainView mainView;

    public MainViewBus() {
    }

    public MainView getMainView() {
        return this.mainView;
    }
    public void setMainView(MainView mainView) {
        this.mainView = mainView;
    }
}
public class MainView implements RouterLayout {
	public MainView(MainViewBus mainViewBus){
		mainViewBus.setMainView(this);
	}
	
	public void someMethod(){
		Notification.show("You just invoked a method on MainView from a child view.");
	}
}
@Route("foo", layout = MainView.class)
public class FooView extends VerticalLayout {
	public FooView(MainViewBus mainViewBus){
		add(new Button("Test MainView Bus", click -> mainViewBus.getMainView().someMethod()));
	}
}