Search code examples
javavaadincdimvp

How to implement MVP using Vaadin CDI and Navigator?


I would like to implement the MVP pattern in my web application using Vaadin. I am using the Vaadin Navigator and the CDIViewProvider, something like so:

//MyUI.java
public class MyUI extends UI {
    @Inject
    CDIViewProvider viewProvider;

    Navigator navigator;

    @Override
    protected void init(VaadinRequest vaadinRequest) {

        //UI setup
        navigator = new Navigator(this, someContainer);
        navigator.addProvider(viewProvider);
        getNavigator().navigateTo("myView");
    }
}

//MyViewImpl.java
@CDIView("myView")
public class MyViewImpl extends VerticalLayout implements com.vaadin.navigator.View, MyView {
    // ...

To my understanding the view provider instantiates an object of MyViewImpl which is ViewScoped, i.e. it gets created/destroyed as the user navigates to/out of this view. Next I need to think about where to instantiate the presenter of this view. I thought about making it SessionScoped and inject it to the view:

// ... continue MyViewImpl.java

@Inject 
private MyViewListener presenter;

// in some of the MyViewImpl methods we can do stuff like presenter.buttonClicked();
}

//MyViewPresenter.java
@SessionScoped
public class MyViewPresenter implements MyView.MyViewListener {

    MyView view; //how can the presenter obtain the view here ?

    @PostConstruct
    public void init() 
    @Override
    public void buttonClicked() {
        view.displaySomething();
    }
}

//MyView.java
public interface MyView {
    public interface MyViewListener {     
        public void buttonClicked();
    }

    public void displaySomething();
}

As I already pointed out in the comment behind the presenters reference to the view I do not know how the presenter can get a hold of the view instance (which is created by the view provider). Does it even make sense to have the presenter be SessionScoped, while the views get created/destroyed whenever the view changes ? An alternative way would be that the view does not have a reference to the presenter, but that the presenter adds itself as the listener to the view. However in this case I again do not know where to instantiate the presenter and how to get a reference to the current view.


Solution

  • Don't try to inject the view, use a view setter in presenter instead and call it from view's @PostConstruct:

    @Dependent
    class MyPresenter {
    
        private MyView view;
    
        public void setView(MyView view) {
            this.view = view;
        }
    }
    
    public interface MyView {
    }
    
    @CDIView("my-view")
    public class MyViewImpl extends VerticalLayout implements View, MyView {
    
        @Inject
        private MyPresenter presenter;
    
        @PostConstruct
        private void init() {
            presenter.setView(this);
        }
    }
    

    I think that creation order is the confusing issue here. In a way views have higher priority as they are created by Vaadin when you call getNavigator().navigateTo("my-view"). Presenter is instantiated by the view by CDI (even though it supervises the view) and should have @Dependent scope.