How to conditionally render plain HTML elements like <div>s?
I'm trying to implement a composite component which either displays the information details of a user in plain text or displays them through editable input texts fields if the desired details are those of the user currently connected.
I know that al UI Components can be rendered via the rendered attribute but what about the ones which are not UI Components (for example divs)
<div class = "userDetails" rendered = "#{cc.attrs.value.id != sessionController.authUser.id}">
Name: #{cc.attrs.value.name}
Details: #{cc.attrs.value.details}
</div>
<div class = "userDetails" rendered = "#{cc.attrs.value.id == sessionController.authUser.id}">
<h:form>
...
</h:form>
</div>
I know that the div doesn't have the rendered attribute and probably I'm not taknig the right approach at all. I could very easily use an JSTL tag but I want to avoid that.
Solution 1:
The right JSF component to represent a HTML <div>
element is the <h:panelGroup>
with the layout
attribute set to block
. So, this should do:
<h:panelGroup layout="block" ... rendered="#{someCondition}">
...
</h:panelGroup>
Alternatively, wrap it in an <ui:fragment>
:
<ui:fragment rendered="#{someCondition}">
<div>
...
</div>
</ui:fragment>
Or when you're already on JSF 2.2+, make it a passthrough element:
<div jsf:rendered="#{someCondition}">
</div>
Do note that when you'd like to ajax-update a conditionally rendered component, then you should be ajax-updating its parent component instead.
See also:
- Alternative to ui:fragment in JSF
- Conditional rendering of non-JSF components (plain vanilla HTML and template text)
- Ajax update/render does not work on a component which has rendered attribute
- Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
- Is it possible to update non-JSF components (plain HTML) with JSF ajax?
Solution 2:
This has been easy since JSF 2.2. By using pass-through elements, any HTML element can be converted to a JSF component, which has the rendered attribute.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsf="http://xmlns.jcp.org/jsf">
<div class="userDetails" jsf:rendered="#{cc.attrs.value.id != sessionController.authUser.id}">
Name: #{cc.attrs.value.name}
Details: #{cc.attrs.value.details}
</div>
</html>
Read more at https://jsflive.wordpress.com/2013/08/08/jsf22-html5/#elements
Solution 3:
I would just wrap your HTML with <h:panelGroup>
<h:panelGroup rendered = "#{cc.attrs.value.id != sessionController.authUser.id}">
<div class = "userDetails">
Name: #{cc.attrs.value.name}
Details: #{cc.attrs.value.details}
</div>
</h:panelGroup>
<h:panelGroup rendered = "#{cc.attrs.value.id == sessionController.authUser.id}">
<div class = "userDetails">
<h:form>
...
</h:form>
</div>
</h:panelGroup>
Another option is to use components from either Seam (<s:div>
) or Tomahawk (<t:htmlTag>
) libraries if you already have them in your project.
See: http://www.jsftoolbox.com/documentation/seam/09-TagReference/seam-div.html
<s:div styleClass = "userDetails" rendered = "#{cc.attrs.value.id != sessionController.authUser.id}">
Name: #{cc.attrs.value.name}
Details: #{cc.attrs.value.details}
</s:div>
<s:div styleClass = "userDetails" rendered = "#{cc.attrs.value.id == sessionController.authUser.id}">
<h:form>
...
</h:form>
</s:div>
Or: http://myfaces.apache.org/tomahawk-project/tomahawk12/tagdoc/t_htmlTag.html
<t:htmlTag value="div" styleClass = "userDetails" rendered = "#{cc.attrs.value.id != sessionController.authUser.id}">
Name: #{cc.attrs.value.name}
Details: #{cc.attrs.value.details}
</t:htmlTag>
<t:htmlTag value="div" styleClass = "userDetails" rendered = "#{cc.attrs.value.id == sessionController.authUser.id}">
<h:form>
...
</h:form>
</t:htmlTag>