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

2

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)

Furmenty answered 3/6, 2016 at 19:36 Comment(0)
G
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();
}
Grotius answered 3/6, 2016 at 19:42 Comment(1)
That's what I did for now, thanks! But isn't there a more abstract way?Furmenty
R
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>
Radley answered 13/10, 2017 at 19:43 Comment(2)
Great! I'm thinking: isnt't there a method like component.children.size()? I'll look for it...Furmenty
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.Radley
G
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();
}
Grotius answered 3/6, 2016 at 19:42 Comment(1)
That's what I did for now, thanks! But isn't there a more abstract way?Furmenty

© 2022 - 2024 — McMap. All rights reserved.