Search code examples
jsfliferayliferay-dxp

Storing a Liferay 7 portlet preference using a JSF selectOneListbox


I'm upgrading a JSF portlet we had in Liferay 6.2 to Liferay 7.

The portlet displays a list of icons and a selectOneListbox used to control how those icons are displayed.

<h:selectOneListbox id="listModeSelector" value="#{user.listMode}" size="1">
    <f:selectItems value="#{user.listModes}" var="mode"
        itemLabel="#{mode.label}" itemValue="#{mode.value}" />
    <f:ajax event="change" execute="@this" render=":metricsPanel" />
</h:selectOneListbox>

When user.setListMode is called after a change to the selectOneListbox, the portlet would save the new option to portlet preferences, with a call to the bean's PortletPreferences' setValue and store functions:

@ManagedBean
@SessionScoped
public class User {
    private static final String LIST_MODE_KEY = "listMode";
    private ListMode listMode;
    private PortletPreferences preferences;

    public User() {
        PortletRequest request = ((PortletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest());
        preferences = request.getPreferences();
        listMode = ListMode.fromValue( preferences.getValue( LIST_MODE_KEY, ListMode.Normal.getValue() ) );
    }

    public String getListMode() {
        return listMode.getValue();
    }

    public ListMode[] getListModes() {
        return ListMode.values();
    }

    public void setListMode( String listModeValue ) {
        this.listMode = ListMode.fromValue( listModeValue );
        try { 
            preferences.setValue( LIST_MODE_KEY, listModeValue );
            preferences.store();
        }
        catch ( ...Exception e ) {
            log.error( "unable to persist listMode: " + e.getMessage(), e );
        }

    }

}

When they change this setting, we want it to stay changed for them, for any future sessions. But since moving to Liferay 7, doing this causes an IllegalStateException with the message Preferences cannot be stored inside a render call.

So my question is: in Liferay 7 JSF, is there a way to store PortletPreferences from a change to an item like a selectOneListbox, rather than submitting a form? If not, what would be the proper way to do this?


Solution

  • You should always use the portlet preferences of the current request. As you use the preferences from the constructor of your session bean, which is usually called from the render request first, the preferences are still connected with the (outdated) render request.

    I mean like this:

    public void setListMode( String listModeValue ) {
        this.listMode = ListMode.fromValue( listModeValue );
        try { 
            PortletPreferences preferences = ((PortletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest()).getPreferences();
            preferences.setValue( LIST_MODE_KEY, listModeValue );
            preferences.store();
        }
        ...
    }