4

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?

Tobias Liefke
  • 8,637
  • 2
  • 41
  • 58
Sechanris
  • 232
  • 1
  • 9
  • 1
    Can you add a bit of code? I mean, how do you bind your bean to the `selectOneListbox`? I'm running JSF on Liferay 7 as well and everything before the _Render Response Phase_ is still called in the action call of the portlet. – Tobias Liefke Mar 19 '18 at 20:38
  • Added my xhtml and bean. – Sechanris Mar 20 '18 at 14:22

1 Answers1

2

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();
    }
    ...
}
Tobias Liefke
  • 8,637
  • 2
  • 41
  • 58