6

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)

banan3'14
  • 3,810
  • 3
  • 24
  • 47
The Student
  • 27,520
  • 68
  • 161
  • 264

2 Answers2

4

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>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    Great! I'm thinking: isnt't there a method like `component.children.size()`? I'll look for it... – The Student Oct 20 '17 at 15:09
  • 1
    Yup, exactly that, or `#{component.childCount}` for more effectiveness. It only isn't helpful in solving your specific problem as it doesn't consult the children's `rendered` attribute. – BalusC Oct 20 '17 at 17:19
2

i would shortly go with this:

<h:panelGroup rendered="{bean.isSometimes()}"/>
  <h:outputText value="title" />
  <h:itemThatSometimesWillShow rended="{bean.isSometimes1()}" />
  <h:itemThatSometimesWillShow rended="{bean.isSometimes2()}" />
  <h:itemThatSometimesWillShow rended="{bean.isSometimes3()}" />

and in the bean:

public boolean isSometimes()
{
    return isSometimes1() || isSometimes2() || isSometimes3();
}
tt_emrah
  • 1,043
  • 1
  • 8
  • 19