I'm trying to use <c:if> to conditionally put a <h:outputLink> inside a <h:dataTable> when the state is finished.
<h:dataTable value="#{bean.items}" var="item" width="80%">
<h:column>
<f:facet name="header">
<h:outputText value="State" />
</f:facet>
<c:if test="#{item.state != 'Finish'}">
<h:outputText value="Missing value" />
</c:if>
<c:if test="#{item.state == 'Finish'}">
<h:outputLink value="myLink">
<h:outputText value="Value = #{item.state}" />
</h:outputLink>
</c:if>
</h:column>
</h:dataTable>
But this does not work, why is that and how can I fix it?
JSTL tags are evaluated during building of the view, not during rendering of the view. You can visualize it as follows: Whenever a view tree get created for the first time, all JSTL tags are executed and the result is a view with only JSF components. Whenever a view tree get rendered, all JSF components get executed and the result is HTML. So: JSF+JSTL doesn't run in sync as you'd expect from the coding. JSTL runs from top to bottom first, hands the result to JSF and then it's JSF's turn to run from top to bottom again. This may lead to unexpected results in JSF iterating components like UIData because the row data (in your particular case the #{item} object) is not available while JSTL runs.
In a nutshell: Use JSTL to control flow of JSF component tree building. Use JSF to control flow of HTML output generation.
You want to use the rendered attribute here.
<h:outputText value="Missing value" rendered="#{item.state ne 'Finish'}" />
<h:outputLink value="myLink" rendered="#{item.state eq 'Finish'}">
<h:outputText value="Value = #{item.state}" />
</h:outputLink>
See also:
JSTL in JSF2 Facelets... makes sense?
Conditionally displaying JSF components
Related
I'm trying to use <c:if> to conditionally put a <h:outputLink> inside a <h:dataTable> when the state is finished.
<h:dataTable value="#{bean.items}" var="item" width="80%">
<h:column>
<f:facet name="header">
<h:outputText value="State" />
</f:facet>
<c:if test="#{item.state != 'Finish'}">
<h:outputText value="Missing value" />
</c:if>
<c:if test="#{item.state == 'Finish'}">
<h:outputLink value="myLink">
<h:outputText value="Value = #{item.state}" />
</h:outputLink>
</c:if>
</h:column>
</h:dataTable>
But this does not work, why is that and how can I fix it?
JSTL tags are evaluated during building of the view, not during rendering of the view. You can visualize it as follows: Whenever a view tree get created for the first time, all JSTL tags are executed and the result is a view with only JSF components. Whenever a view tree get rendered, all JSF components get executed and the result is HTML. So: JSF+JSTL doesn't run in sync as you'd expect from the coding. JSTL runs from top to bottom first, hands the result to JSF and then it's JSF's turn to run from top to bottom again. This may lead to unexpected results in JSF iterating components like UIData because the row data (in your particular case the #{item} object) is not available while JSTL runs.
In a nutshell: Use JSTL to control flow of JSF component tree building. Use JSF to control flow of HTML output generation.
You want to use the rendered attribute here.
<h:outputText value="Missing value" rendered="#{item.state ne 'Finish'}" />
<h:outputLink value="myLink" rendered="#{item.state eq 'Finish'}">
<h:outputText value="Value = #{item.state}" />
</h:outputLink>
See also:
JSTL in JSF2 Facelets... makes sense?
Conditionally displaying JSF components
I have following code:
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<h:form id="#{class2.name}">
<h:outputText value="#{class2.name}" />
</h:form>
</ui:repeat>
However, when I open the page, it errors as follows:
component identifier must not be a zero-length String
But it is properly printed in the <h:outputText>. How is this caused and how can I solve it?
You can use EL in the id attribute of a JSF component, but the EL variable has to be available during view build time, while the JSF component tree is to be built. However, the <ui:repeat> runs during view render time, while the HTML output is to be generated based on JSF component tree. The <ui:repeat var> is not available during view build time and #{class2.name} evaluates to null which totally explains the error you got. That it works in <h:outputText> is because it runs during view render time.
If you replace <ui:repeat> by <c:forEach>, which runs during view build time, then it'll work as you intented. The <c:forEach> will namely generate physically multiple <h:form> components in the JSF component tree which each generate individually their own HTML output (in contrary to <ui:repeat>, wherein the very same <h:form> component is been reused multiple times to generate HTML output).
<c:forEach var="class2" items="#{bean.list}" varStatus="status">
<h:form id="#{class2.name}">
<h:outputText value="#{class2.name}" />
</h:form>
</c:forEach>
However, I really wonder why you need to do that. There's usually no need to dynamically assign component IDs. JSF will already ensure the uniqueness of the ID. The below example,
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<h:form id="form">
<h:outputText value="#{class2.name}" />
</h:form>
</ui:repeat>
will end up in multiple forms with each an unique ID, suffixed with iteration index of the <ui:repeat>. If you actually need to use #{class2.name} for some JavaScript/jQuery purposes (you did nowhere state the concrete functional requirement in the question for which you thought that this would be the right solution, so it's merely guessing), then just wrap it in a plain vanilla HTML element:
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<div id="#{class2.name}">
<h:form id="form">
<h:outputText value="#{class2.name}" />
</h:form>
</div>
</ui:repeat>
Or set it as style class of a JSF component, which is also just selectable via a CSS selector:
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<h:form id="form" styleClass="#{class2.name}">
<h:outputText value="#{class2.name}" />
</h:form>
</ui:repeat>
See also:
JSTL in JSF2 Facelets... makes sense?
I have following code:
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<h:form id="#{class2.name}">
<h:outputText value="#{class2.name}" />
</h:form>
</ui:repeat>
However, when I open the page, it errors as follows:
component identifier must not be a zero-length String
But it is properly printed in the <h:outputText>. How is this caused and how can I solve it?
You can use EL in the id attribute of a JSF component, but the EL variable has to be available during view build time, while the JSF component tree is to be built. However, the <ui:repeat> runs during view render time, while the HTML output is to be generated based on JSF component tree. The <ui:repeat var> is not available during view build time and #{class2.name} evaluates to null which totally explains the error you got. That it works in <h:outputText> is because it runs during view render time.
If you replace <ui:repeat> by <c:forEach>, which runs during view build time, then it'll work as you intented. The <c:forEach> will namely generate physically multiple <h:form> components in the JSF component tree which each generate individually their own HTML output (in contrary to <ui:repeat>, wherein the very same <h:form> component is been reused multiple times to generate HTML output).
<c:forEach var="class2" items="#{bean.list}" varStatus="status">
<h:form id="#{class2.name}">
<h:outputText value="#{class2.name}" />
</h:form>
</c:forEach>
However, I really wonder why you need to do that. There's usually no need to dynamically assign component IDs. JSF will already ensure the uniqueness of the ID. The below example,
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<h:form id="form">
<h:outputText value="#{class2.name}" />
</h:form>
</ui:repeat>
will end up in multiple forms with each an unique ID, suffixed with iteration index of the <ui:repeat>. If you actually need to use #{class2.name} for some JavaScript/jQuery purposes (you did nowhere state the concrete functional requirement in the question for which you thought that this would be the right solution, so it's merely guessing), then just wrap it in a plain vanilla HTML element:
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<div id="#{class2.name}">
<h:form id="form">
<h:outputText value="#{class2.name}" />
</h:form>
</div>
</ui:repeat>
Or set it as style class of a JSF component, which is also just selectable via a CSS selector:
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<h:form id="form" styleClass="#{class2.name}">
<h:outputText value="#{class2.name}" />
</h:form>
</ui:repeat>
See also:
JSTL in JSF2 Facelets... makes sense?
In my JSF page I use <c:choose><c:when> tag to conditionally display content. However, it does not work as it seems to always evaluate false.
E.g.
<h:outputText value="#{pollInfo.active} -" />
<c:choose>
<c:when test="#{pollInfo.active}">
<h:outputText value="Active" />
</c:when>
<c:otherwise>
<h:outputText value="Deactive" />
</c:otherwise>
</c:choose>
There are for sure a few items with active=true, which is confirmed by <h:outputText>, but it just prints Deactive for all items. You can see the actual output in the following picture:
How is this caused and how can I solve it?
The symptoms and the screenshot suggests that the #{pollInfo} represents the currently iterated item of an iterating UI component such as <ui:repeat>, <h:dataTable>, <p:tabView>, <p:dataTable>, etc.
JSTL tags run during view build time, that moment when JSF component tree is built based on XHTML source code. The var attribute of such an iterating UI component is only available during view render time, that moment when HTML output is produced based on JSF component tree.
In other words, they don't run "in sync". The #{pollInfo} is always null during view build time.
In this particular case, you need the JSF component's rendered attribute instead.
<h:outputText value="Active" rendered="#{pollInfo.active}" />
<h:outputText value="Deactive" rendered="#{not pollInfo.active}" />
Or if you intend to conditionally render larger pieces of code, wrap all in a <ui:fragment>:
<ui:fragment rendered="#{pollInfo.active}">
<h:outputText value="Active" />
<!-- Some more components here if necessary. -->
</ui:fragment>
<ui:fragment rendered="#{not pollInfo.active}">
<h:outputText value="Deactive" />
<!-- Some more components here if necessary. -->
</ui:fragment>
Again another alternative, given that you've a pure if-else, is using the conditional operator in EL:
<h:outputText value="#{pollInfo.active ? 'Active' : 'Deactive'}" />
Additional bonus is, you end up with much less code.
See also:
JSTL in JSF2 Facelets... makes sense?
I am using JSF 2.1 and ICEFaces 2 and I have a tabset like:
<ace:tabSet clientSide="true">
<ace:tabPane>
<f:facet name="label">My First Tab</f:facet>
<h:outputText value="One"></h:outputText>
</ace:tabPane>
<ace:tabPane>
<f:facet name="label">Second Tab</f:facet>
<h:outputText value="Two"></h:outputText>
</ace:tabPane>
<ace:tabPane>
<f:facet name="label">Third Tab</f:facet>
<h:outputText value="Third"></h:outputText>
</ace:tabPane>
</ace:tabSet>
Instead of hardcoding the tabPanes, I want to generate them dynamically based on a list. How can I achieve this by standard JSF or ICEFaces components?
I tried to use the <ui:repeat>, but it doesn't work:
<ui:repeat var="o" value="#{bean.myList}" varStatus="status">
<ace:tabPane>
<f:facet name="label">#{o.name}</f:facet>
<h:selectManyCheckbox value="#{o.valuesArray}" layout="pageDirection">
<f:selectItems value="#{o.checkboxes}" />
</h:selectManyCheckbox>
</ace:tabPane>
</ui:repeat>
I don't do ICEFaces, but you're encountering basically the same problem as RichFaces has with its <rich:tab>. As there exist no hypothetical <rich:tabs> (like as <rich:columns> which would be the dynamic variant of <rich:column>/<h:column>), one would have to use the view build time JSTL <c:forEach> tag to prepare the JSF view with those tags. You could apply the same on your ICEFaces page:
<ace:tabSet clientSide="true">
<c:forEach items="#{bean.tabs}" var="tab">
<ace:tabPane>
<f:facet name="label">#{tab.label}</f:facet>
<h:outputText value="#{tab.name}"></h:outputText>
</ace:tabPane>
</c:forEach>
</ace:tabSet>
Note that this will only fail if #{bean} is not a real managed bean, but an expression which in turn depends on an evaluation of another JSF component, such as <h:dataTable var="bean">.
The <ui:repeat> won't work as it's a render-time tag. It won't dynamically generate multiple JSF components before the JSF component tree is been built.
See also:
ICEFaces issue 6527 - Dynamically adding/removing ace:tabPane Results in ace:tabset Retrieving Entire Tabset Content
ICEFaces issue 6560 - New ace:tabs dynamic tabset component
i think you can write like this:
<ice:panelTabSet var="tab" value="#{mainTabsTempl.tabs}">
<ice:panelTab label="#{tab.label}">
body
</ice:panelTab>
</ice:panelTabSet>
It's not tabSet tag, but have the same work.