Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Missing parameter values in invoked method with composite components using ui:repeat

So after several days of debugging, we were eventually able to reproduce some strange interactions between composite components, ui:repeat, p:remoteCommand and partial state saving in JSF that we do not understand.

Scenario

A composite component iterates over a list of objects using ui:repeat. During each iteration, another composite component is included and arguments are passed.

<ui:composition (...)>
  <ui:repeat var="myVar" value="#{cc.attrs.controller.someList}">
    <namespace:myRemoteCommand someParam="SomeParam"/>

In the included composite component, there is an auto-run p:remoteCommand calling a method using parameters defined in the component's interface.

<ui:component (...)>
  <p:remoteCommand actionListener="#{someBean.someMethod(cc.attrs.someParam)}"
                   autoRun="true"
                   async="true"
                   global="false">

However, when setting a breakpoint in someMethod(...), an empty string is passed. This only happens if partial state saving is set to false.

Solutions

We tried several solutions and the following ones appear to work (however we do not understand why and cannot foresee any further problems that could occur):

  • We can set partial state saving to true.

  • We can change the composite component pattern to ui:include.

  • We can remove one or both of the composite components and directly include the content instead.

Question

Why does JSF behave this way? What is this interaction between composite component, ui:repeat and argument passing that changes depending on whether we use ui:include / partial state saving or not?

We're using Primefaces 5.3, Glassfish 4.1, Mojarra 2.2.12, Java 8.

like image 432
Nephtyz Avatar asked Jun 29 '16 14:06

Nephtyz


1 Answers

Your code is all fine. It's just that Mojarra's <ui:repeat> is broken. You're not the first one facing a state management related problem with <ui:repeat>.

  • Checkbox inside ui:repeat not refreshed by Ajax
  • Dynamically added input field in ui:repeat is not processed during form submit
  • Components are with the same id inside ui:repeat
  • <h:form> within <ui:repeat> not entirely working, only the last <h:form> is processed
  • Composite component with custom backing component breaks strangely when nested inside ui:repeat
  • ui:repeat in o:tree not working as expected

Root cause of your problem is that #{cc} is nowhere available at the moment the <ui:repeat> needs to visit the tree. Effectively, the <ui:repeat value> is null. A quick work around is to explicitly push the #{cc} in UIRepeat#visitTree() method. Given Mojarra 2.2.12, add below lines right before line 734 with pushComponentToEL(facesContext, null).

UIComponent compositeParent = getCompositeComponentParent(this);
if (compositeParent != null) {
    compositeParent.pushComponentToEL(facesContext, null);
}

And add below lines right after line 767 with popComponentFromEL(facesContext).

if (compositeParent != null) {
    compositeParent.popComponentFromEL(facesContext);
}

If you don't build Mojarra from source, copy the entire source code of UIRepeat into your project, maintaining its package structure and apply above changes on it. Classes in /WEB-INF/classes have higher classloading precedence than those in /WEB-INF/lib and server's /lib. I have at least created issue 4162 to address this.

An alternative is to replace Mojarra by MyFaces, or to replace the <ui:repeat> by an UIData based component which got state management right such as <h:dataTable> or <p:dataList>.

<p:dataList type="none" var="myVar" value="#{cc.attrs.controller.someList}">
    <namespace:myRemoteCommand someParam="SomeParam" />
</p:dataList>

You might only want to apply some CSS to get rid of widget style (border and such), but that's trivial.

See also:

  • Should PARTIAL_STATE_SAVING be set to false?
like image 122
BalusC Avatar answered Nov 04 '22 11:11

BalusC