'How to render a JSF component only if at least two children are rendered (title + item)?

I have a:

<h:panelGroup />
  <h:outputText value="title" />
  <h:itemThatSometimesWillShow rendered="sometimes1" />
  <h:itemThatSometimesWillShow rendered="sometimes2" />
  <h:itemThatSometimesWillShow rendered="sometimes3" />
  ...many more

And I would like that, if none of the itemThatSometimesWillShow shows, the whole panel (the title, actually) does not show either.

I did try with composite component's #{cc.childCount} > 1, but I'm not inside a composite implementation, so looks like it will always return 0.

Any idea? (I'm searching for something with js or EL to use in rendered attribute of the parent panelGroup)



Solution 1:[1]

This is achievable with EL 3.0 stream API. My initial attempt was:

<h:panelGroup rendered="#{component.children.stream().filter(c -> c.rendered).count() gt 1}">
    <h:outputText value="title" />
    <h:panelGroup rendered="#{false}">item1</h:panelGroup>
    <h:panelGroup rendered="#{false}">item2</h:panelGroup>
    <h:panelGroup rendered="#{false}">item3</h:panelGroup>
</h:panelGroup>

However, that didn't work quite well. It unexpectedly ran into an infinite loop which ultimately ended with an OutOfMemoryError. It appears that #{component} EL variable still represents the previous component at the moment the rendered attribute is consulted. This is a bit of a chicken-egg issue: the #{component} for current component is only injected if its rendered attribute evaluates true.

Given that, I can see two more options: explicitly find the component by ID as below,

<h:panelGroup id="foo" rendered="#{component.findComponent('foo').children.stream().filter(c -> c.rendered).count() gt 1}">
    <h:outputText value="title" />
    <h:panelGroup rendered="#{false}">item1</h:panelGroup>
    <h:panelGroup rendered="#{false}">item2</h:panelGroup>
    <h:panelGroup rendered="#{false}">item3</h:panelGroup>
</h:panelGroup>

or let it print some CSS class which in turn does a display:none;.

<h:panelGroup styleClass="#{component.children.stream().filter(c -> c.rendered).count() gt 1 ? 'show' : 'hide'}">
    <h:outputText value="title" />
    <h:panelGroup rendered="#{false}">item1</h:panelGroup>
    <h:panelGroup rendered="#{false}">item2</h:panelGroup>
    <h:panelGroup rendered="#{false}">item3</h:panelGroup>
</h:panelGroup>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1