Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)

like image 410
The Student Avatar asked Jun 03 '16 19:06

The Student


2 Answers

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>
like image 90
BalusC Avatar answered Oct 24 '22 02:10

BalusC


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();
}
like image 29
tt_emrah Avatar answered Oct 24 '22 02:10

tt_emrah