CDI Add-on @UiScoped View

I was wondering is there any way to make View @UiScoped?

In a current version of CDI addon (1.0.0.beta4) @CDIView is @ViewScoped. I tried to add @UiScoped to view, but View is created twice, and after quick look in addon source, I realized that adding annotation won’t help.

I have also read discussion at http://dev.vaadin.com/ticket/12378 and I agree that @ViewScoped is good solution, but I also need @UiScoped view.

As a workaround, I’m using @UiScoped custom component injected into view. Is this recomended way to go?

You should be able to just annotate your view with @UIScoped to have that override the default @ViewScoped. If that’s not the case, please file a bug ticket so we can get it fixed.

My mistake. I also use cdi-mvp add-on, and that is the problem.

When I create only View annotated as @UIScoped, everything is fine.
When I use cdi-mpv (modified, AbstractMVPView to suport navigation) instance of View is created twice.

@CDIView(value=ViewToken.VIEW_HOME, supportsParameters=true, uis={MainUI.class})
@UIScoped
public class HomeViewImpl extends AbstractNavigatableMVPView implements HomeView {

    @PostConstruct
    protected void initView() {
        LOG.error("NEW INSTANCE CREATED");
    }
}


@ViewInterface(HomeView.class)
@UIScopedStereotype
public class HomePresenter extends AbstractMVPPresenter<HomeView> {

    @PostConstruct
    @Override
    protected void postConstruct() {
        super.postConstruct();
        LOG.error("Presenter new instance");    
    }

    @Override
    public void viewEntered() {
    }
}

I suspect that problem lies somwhere in MVPExtension.java in mvp add-on.

Yes. I made a small modification to the cdi-mvp version I’m using to address this.
As a note glassfish 4.1 ( WELD 2.2.2 Final ) wont even deply an application with an MVPView that is annotated with @UIScoped.

In my case I implemented a ViewDisplay as an MVPView ( and this needs to be UIScoped ).
The solution is to make a copy of AbstractMVPPresenter ( with @UIScopedStereotype instead of @ViewScopedStereotype )

You will need some modifications to MVPExtension to handle the UIScoped View and Presenter…

My ViewDisplay is already @UiScoped, and a have modify AbstractMVPPresenter to be @UiScopedStereotype. I’m now stuck with MVPExtension. Can you show me your modifications of MVPExtension?

I’ll see if I can put my changes onto github sometime today. This is by no means a clean solution. But it worked :slight_smile:

I haven’t been active on the project for a while now but if this is something that should be merged in the add-on I’ll accept pull requests on Github.

I have submitted a pull-request from my patched version.

In the meentime Marko you can look at the changes I made in GitHub.

https://github.com/viljoenp/cdi-mvp

Note on my system I do have a problem with the one UnitTest.
When you build , just do a > mvn clean install -DskipTests

Will have a look at that Unit test when I get some time…

I was doing something similar, but with no success.
I have implemented your changes but that also didn’t help.

I’m using MVPView that is also annotated with @CDIView. When I navigate to view, CDI add-on create one view instance, but mvp-addon create another one. I suspect that this is hapening here:

protected void postConstruct() {

        // ViewInterface must be defined

        final Class<? extends MVPView> viewInterface = getClass()

                .getAnnotation(ViewInterface.class).value();

        view = (T) viewInstance.select(viewInterface).get();

        logger.info("Presenter initialized: " + getClass());
}

So, the real problem is how to use mvp-addon with @CDIView and Navigator based navigation?

Finally some progress :slight_smile:

I modified AbstractUIScopedMVPPresenter and @ViewInterface annotation. Here is relevant code:

@UIScopedStereotype
public abstract class AbstractUIScopedMVPPresenter<T extends MVPView> implements
        Serializable, MVPPresenter {

    private transient Logger logger = Logger.getLogger(getClass().getName());

    protected T view;

    @Inject
    private CDIViewProvider viewProvider;

    public static final String VIEW_ENTER = "AbstractUIScopedPresenter_ve";

    @PostConstruct
    protected void postConstruct() {
        view = (T) viewProvider.getView(
               getClass().getAnnotation(ViewInterface.class).viewName());

        logger.info("Presenter initialized: " + getClass());
    }
 
    /**
     * Performs view actions called each time the view is entered.
     */
    public abstract void viewEntered();

    @Target({ ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Inheritet
    public static @interface ViewInterface {
        Class<? extends MVPView> value();

        //mapped view name so that CDIViewProvider can find right View
        String viewName();

    }
}

You should not be using AbstractUIScopedMVPPresenter or AbstractUIScopeMVPView for CDIViews, for a CDIView (CDIViews would imply a ViewScoped ) you should use the normal AbstractMVPPresenter and AbstractMVPView. If you implement a ViewDisplay ( that is injected into the UI itstelf you will need to use AbstractUIScopedMVPPresenter and AbstractUIScopeMVPView.

But I’m thinking this can be done more elegantly.

As Marcus said, CDIView can be annotated as @UiScoped and override default @ViewScoped scope. I tested this and it works as expected.
In my project I’m using cd-mvp, and all of my CDIView-s are also MVPView-s.
I have tested all posible combinations and only working solution is to modify AbstractMVPresenter to use CDIViewProvider to get reference to MVPView (if MVPView is @CDIView). For this situation I have implemented AbstractNavigatableMVPView and AbstractNavigatableMVPPresenter without scope and stereotype annotations and new @NavigatableViewInterface annotation.

Also, I’m using MVPView without @CDIView to implement nested view logic. Something like this:

@CDIView(value="parent", supportsParameters=true, uis={MainUI.class})
@UiScope
public class ParentViewImpl extends AbstractNavigatableMVPView implements ParentView {
   
    ..............

    @Inject
    private Instance<MVPSubView> childViews;
    

    public void setView(Class<? extends MVPSubView> subView) {
         MVPSubView view = childViews.select(subView).get();
         layout.setContent(view);
         ((AbstractMVPView) view).enter();
    }   
}

In this situation all MVPSubView-s are implemented as @ViewScoped AbstractMVPView.
With CDI addon version 1.0.0.beta4 only the first childView wil be created (when user navigate to ParentView). After that every call to setView method will throw exception.

Caused by: java.lang.IllegalStateException: Can't find proper view for @ViewScoped bean, no views are active for this ui.

I looked into cdi addon source on GitHub and there is a fix for this. I built it and now child views are created in @ViewScope.
If I didn’t miss something, with this modifications everything is working correctly.

Turns out that Vaadin-CDI’s CDIViewProvider can return a wrong instance for views that are in the UI scope. This is probably due to
using the BeanManager with a newly created CreationalContext for fetching the bean
.

I filed a
ticket
about this issue. Also pushed a new
commit
to CDI-MVP’s Github repository including tests that should pass when the bug gets fixed. I’ll publish a new version of the add-on as soon as the issue is resolved.

I will wait for new release of cdi-addon. Meanwhile I will use my workarounds.
Thank you very much

This issue seems not to be resolved! I am currently running in this issue and have no workaround for this behaviour.
I am not using the MVP-Addon. I am using Vaadin CDI 1.0.3 an annoted my class like this:

[code]
@UIScoped
@CDIView(QuestionViewImpl.ID)
public class QuestionViewImpl extends AbstractView implements QuestionView {

@PostConstruct
private void initLayout() {

}
[/code]After that the postconstruct is called twice when i open a window and close it. This destroys all references of my view.