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

1

6

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.

Heaviness answered 29/6, 2016 at 14:18 Comment(0)
U
5

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>.

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:

Unlikely answered 12/7, 2016 at 20:1 Comment(1)
Thank you really much for the in-depth response and analysis. We will just go on with enabled Partial State Saving. I'm glad to know that it is indeed a bug in Mojarra and not us doing something wrong. We spent a long time analyzing the issue until we realized that toggling Partial State Saving solves the problem, but we could not find a reason for this behaviour.Heaviness

© 2022 - 2024 — McMap. All rights reserved.