JSF 2.2 - everything inside ui:repeat is evaluated, although rendered = "false" - jsf

I'm currently migrating a web application from JSF 1.2/Richfaces 3.3.3 to JSF 2.2 For data iteration, we used a4j:repeat from Richfaces. I now want to change the iterators to ui:repeat, because we want to throw out Richfaces.
However, I came across a very strange behaviour. Imagine a code snippet like this (simplified from the original):
<ui:repeat id="criterions" var="criterion" value="#{AdvancedSearchBean.criterionList}">
<h:panelGroup rendered="#{criterion.searchCriterion.displayType == 'PERSON'}">
<h:inputText value="#{criterion.searchString}"/>
</h:panelGroup>
</ui:repeat>
The part inside the panelGroup is evaluated, although the rendered condition definitely evaluates to false. If I change ui:repeat to a4j:repeat, it works fine, the part inside the panelGroup is NOT evaluated.
This is a real problem for our code, as the "criterion" variable can contain different objects (extending the same superclass). In this case, the criterion object does not contain a property with the name "searchString" (because it is not of type "PERSON") -> an error is thrown.
Can anyone explain this behaviour or has a solution?
I'm using the JSF version integrated in WildFly 8.0.0.final (Mojarra 2.2.5-jbossorg-3)
Thanks
Markus

Related

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

primefaces update attribute not working on modal dialog opened from modal dialog [duplicate]

I have a question about the idea behind the fact, that only UIForm got the attribute prependId. Why is the attribute not specified in the NamingContainer interface? You will now probably say that's because of backward compability but I would preferre breaking the compability and let users which implement that interface, also implement methods for the prependId thing.
The main problem from my perspective about the prependId in the UIForm component is, that it will break findComponent()
I would expect that if I use prependId, then the NamingContainer behaviour would change, not only related to rendering but also when wanting to search for components in the component tree.
Here a simple example:
<h:form id="test" prependId="false">
<h:panelGroup id="group"/>
</h:form>
Now when i want to get the panelGroup component I would expect to pass the string "group" to the method findComponent(), but it won't find anything, I have to use "test:group" instead.
The concrete problem with that is, when using ajax with prependId="false". The ajax tag expects in the attributes update and process, that the values care of naming containers. It's a bit strange that when I use prependId="false" that I have to specify the full id or path, but okay.
<h:form id="test" prependId="false">
<h:panelGroup id="group"/>
</h:form>
<h:form id="test1" prependId="false">
<h:commandButton value="go">
<f:ajax render="test:group"/>
</h:commandButton>
</h:form>
Well this code will render without problems but it won't update the panelGroup because it cannot find it. The PartialViewContext will contain only the id "group" as element of the renderIds. I don't know if this is expected, probably it is but I don't know the code. Now we come to the point where the method findComponent() can not find the component because the expression passed as parameter is "group" where the method would expect "test:group" to find the component.
One solution is to write your own findComponent() which is the way I chose to deal with this problem. In this method i handle a component which is a NamingContainer and has the property prependId set to false like a normal UIComponent. I will have to do that for every UIComponent which offers a prependId attribute and that is bad. Reflection will help to get around the static definition of types but it's still not a really clean solution.
The other way would be introducing the prependId attribute in the NamingContainer interface and change the behaviour of findComponent() to work like described above.
The last proposed solution would be changing the behaviour of the ajax tag to pass the whole id, but this would only solve the ajax issue and not the programmatic issues behind the findComponent() implementation.
What do you think about that and why the hell is it implemented like that? I can't be the first having this problem, but I wasn't able to find related topics?!
Indeed, UIComponent#findComponent() as done by <f:ajax render> fails when using <h:form prependId="false">. This problem is known and is a "Won't fix": JSF spec issue 573.
In my humble opinion, they should never have added the prependId attribute to the UIForm during the JSF 1.2 ages. It was merely done to keep j_security_check users happy who would like to use a JSF form with JSF input components for that (j_security_check requires exact input field names j_username and j_password which couldn't be modified by configuration). But they didn't exactly realize that during JSF 1.2 another improvement was introduced which enables you to just keep using <form> for that instead of sticking to <h:form>. And then CSS/jQuery purists start abusing prependId="false" to avoid escaping the separator character : in their poorly chosen CSS selectors.
Just don't use prependId="false", ever.
For j_security_check, just use <form> or the new Servlet 3.0 HttpServletRequest#login(). See also Performing user authentication in Java EE / JSF using j_security_check.
For CSS selectors, in case you absolutely need an ID selector (and thus not a more reusable class selector), simply wrap the component of interest in a plain HTML <div> or <span>.
See also:
How to select JSF components using jQuery?
How to use JSF generated HTML element ID with colon ":" in CSS selectors?
By default, JSF generates unusable ids, which are incompatible with css part of web standards

c:if test eq always returning true

I have to check inserted date and modified date values, which both are string. If they are equal then i wont display edited email or else i will display edited email too. So i have used the below code to validate it.
<b>Created By - </b><h:outputText value="#{o.createdEmail}" /> : <h:outputText value="#{o.createdDateTime}" /><br/>
<c:set var="createdDate" value="#{o.createdDateTime}"/>
<c:set var="modifiedDate" value="#{o.modifiedDateTime}"/>
<c:if test="#{createdDate eq modifiedDate}">
<b>Edited By - </b><h:outputText value="#{o.lastModifiedEmail}" /> : <h:outputText value="#{o.lastModifiedDateTime}" />
</c:if>
Note: o is the variable reference the backend bean.
But it is always displaying it as true even though both values are different. How is this caused and how can I solve it?
Given the way how the code is written (a managed bean with name o makes no utter sense), I do a educated guess that #{o} is declared as var of <h:dataTable> or <ui:repeat>. If that is indeed the case, then that would totally explain the symptoms. JSTL tags runs during view build time, that moment when the JSF component tree is built based on XHTML source code. However, JSF components such as <h:dataTable> and <ui:repeat> runs during view render time, that moment when the JSF component tree needs to produce HTML output.
So, in effects, the #{o} is not available at the moment JSTL <c:if> runs during view build time. You should instead be using a JSF component with rendered attribute which runs during view render time, the same moment as #{o} is been put in the EL variable scope based on current iteration round.
<ui:fragment rendered="#{createdDate eq modifiedDate}">
<b>Edited By - </b><h:outputText value="#{o.lastModifiedEmail}" /> : <h:outputText value="#{o.lastModifiedDateTime}" />
</ui:fragment>
Note: this doesn't affect <c:set>. It merely creates an "alias" to the EL expression, it doesn't immediately evaluate the EL expression, so the <c:set> is completely safe here, albeit somewhat unnecessary as the value doesn't represent such a complex EL expression.
See also:
JSTL in JSF2 Facelets... makes sense?

JSF: Unable to set null values in h:inputText within ui:repeat

Mojarra 2.1.6 / Glassfish 3.1.2
I have something like this
<ui:repeat var="item" value="#{bean.items}">
<h:inputText value="#{item.longProperty}"/>
</ui:repeat>
where Item is an object with a Long property
public class Item {
private Long longProperty; // + getters/setters
}
It works fine as long as I'm setting longProperty to something not-null / non-empty.
When I do that, the null input is ignored and the old value is restored.
Similar results for Dates.
It makes no difference if I eliminate the reference to the var/status from the ui:repeat. Even explicitly referencing #{bean.items[0].longProperty} fails the same way.
The same exact h:inputText works fine outside of a ui:repeat and null/empty values work just fine.
Additionally, inside a ui:repeat, empty strings work ok, if the property is a String rather than an object that requires conversion.
To make it even weirder: if I put a valueChangeEvent on the input, I do see that event.getNewValue() returns null when I expect it - yet my setter is called with the old value.
Any idea what's going on?
Turns out its a Mojarra / JSF implementation bug with ui:repeat
http://java.net/jira/browse/JAVASERVERFACES-1721
And duplicated now, here (by Roger Kitain):
http://java.net/jira/browse/JAVASERVERFACES-2717
Change <ui:repeat> to <a4j:repeat> .
<a4j:repeat var="item" value="#{bean.items}">
<h:inputText value="#{item.longProperty}"/>
</a4j:repeat>
More info: Richfaces a4j:repeat.
See more about the <ui:repeat> bug at Problem with ui:repeat and NULL values in JSF 2.x.

How to set value in bean using use c:set with jsf?

I have another problem to solve. I have a code on my xhtml page:
<t:dataList id="myDataList" value="#{myBean.myList}" var="element" first="0" `rows="10" dir="LTR" frame="hsides" rules="all">`
<c:set target="#{myBean}" property="fid" value="#{element[0]}"/>
...
</t:dataList>
The problem is that value of 'fid' in a bean is null when I`m checking it. When I wrote:
<c:set target="#{myBean}" property="fid" value="8"/>
everything works fine and value is set to '8'. How should i fix this? Thanks for replies.
JSTL tags runs during view build time. JSF tags runs during view render time. You can visualize it as follows: JSTL runs from top to bottom first when the JSF component tree is to be populated, then hands over the component tree to JSF and finally JSF runs from top to bottom to render the HTML.
The element[0] is not there when JSTL is creating the view. It's only there when JSF is rendering the HTML. However, 8 is hardcoded and it is always there.
There are several ways to achieve it the proper way, but since the functional requirement is unclear, I can't suggest a proper approach. Maybe you need f:setPropetyActionListener. Maybe you need DataModel#getRowData() or UIData#getRowData(). Or maybe you don't need it at all.

Resources