Search code examples
javajsfcdi

Changing reference of ViewScoped beans in JSF 2.3 dynamically


I am working on an application in JSF, in which I have two ViewScoped beans within the same screen, and they need to communicate with each other. The first bean represents the main screen, and the other bean is a popup window where the user selects a certain value, which is then passed back to the previous bean.

The problem is that this latter bean is generic, meaning it can be used from various screens, so it doesn't have a direct reference to the main screen's bean. Instead, it references an interface, which is set dynamically. However, in some cases, this dynamic setting leads to errors, such as data loss.

Example:

public interface PopupCallback {
    void onValueSelected(String value);
}

@Named
@ViewScoped
public class MainBean implements PopupCallback {
    private String selectedValue;

    // Getter and Setter for selectedValue

    @Override
    public void onValueSelected(String value) {
        this.selectedValue = value;
        // You can perform any additional logic here upon receiving the value from the popup.
    }

    public void openPopup() {
        // Code to open the generic popup window
    }
}

@Named
@ViewScoped
public class GenericPopupBean {
    private PopupCallback callback;

    // Setter for the callback interface
    public void setCallback(PopupCallback callback) {
        this.callback = callback;
    }

    // Method to be called when the user selects a value in the popup
    public void onValueSelected(String value) {
        if (callback != null) {
            callback.onValueSelected(value);
        }
    }
}

What is the best way to achieve this?


Solution

  • If this were me, I would just inject the bean:

    @Named
    @ViewScoped
    public class GenericPopupBean {
        @Inject
        private PopupCallback callback;
    }
    

    This should work as long as there is one implementation of PopupCallback. You wouldn't even need the PopupCallback interface.

    If multiple implementations of PopupCallback exist, I would probably solve this by inverting the relationship:

    @Named
    @ViewScoped
    public class MainBean implements PopupCallback {
        @Inject
        private MainBean popupCallback;
        @Inject
        private GenericPopupBean genericPopupBean;
    
    ...
    
        public void openPopup() {
            genericPopupBean.setCallBack(popupCallback); //note 'this' might work here instead, depending on the CDI implementation. Not sure that's 100% spec compliant.
            ...
        }
    ...
    }
    

    If you want to know why this works, see my answer here: Why does self injection with CDI not result in an infinite loop?

    It's usually not a good idea to pass around @Injected material, but in this particular case I think it's ok.

    Other ways would be to use the flash scope, which really isn't super great solution if people have multiple tabs/windows open.

    Another is CDI Events, where you broadcast an event and then an arbitrary number of receivers can listen for said event, but it'll be hard to figure out who the correct receiver should be if you have multiple PopupCallbacks.

    Anyway good luck, let us know what you end up doing so others can benefit!