22

I would like to be able to conditionally omit a footer from a PrimeFaces panel element:

<p:panel header="some text">
    <f:facet name="footer">
        #{message}
    </f:facet>
    <!-- ... -->
</p:panel>

I hoped that the rendered attribute would work:

<p:panel header="some text">
    <f:facet name="footer" rendered="#{!empty message}">
        #{message}
    </f:facet>
    <!-- ... -->
</p:panel>

But the footer is still rendered, with empty content. It appears that facet does not have the rendered attribute: http://www.jsftoolbox.com/documentation/help/12-TagReference/core/f_facet.html.

What's the right way to do this?

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • 3
    I did some tests: a footer without any content (whitespace allowed) won't be rendered. A footer with *any* content, even though it's a simple EL which returns null/empty, then it renders the footer anyway. I am not sure which behaviour is desired, might be worth a report at PF issue tracker: http://code.google.com/p/primefaces/issues/ – BalusC Dec 06 '10 at 20:02
  • @BalusC: looks like it's bug report time. – Matt Ball Dec 06 '10 at 20:11

12 Answers12

15

I was able to solve this by swapping the facet out for an attribute. To summarize:

This works

<p:panel ...>
    <f:attribute name="footer" value="#{message}"/>
    <!-- ... -->
</p:panel>

But this doesn't work

<p:panel footer="#{message}">
    <!-- ... -->
</p:panel>

Neither does this

<p:panel ...>
    <f:facet name="footer">#{message}</f:facet>
    <!-- ... -->
</p:panel>

Nor this

<p:panel ...>
    <f:facet name="footer">
        <h:outputText value="#{message}" rendered="#{!empty message}"/>
    </f:facet>
    <!-- ... -->
</p:panel>

by "works" I mean:

"renders no footer — not just an empty footer — when #{message} is empty or null; otherwise, correctly renders the footer with the specified text."


PrimeFaces forum thread on this issue

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • 1
    Oh, interesting one. How did you find it out? Lucky accident? Digged the renderer source? – BalusC Dec 06 '10 at 21:41
  • 2
    @BalusC: trial and error - so, sure, lucky accident :) having been learning JSF et al for <3 weeks (PrimeFaces for <1 week) I don't think I'm quite up to digging through the source code yet. – Matt Ball Dec 06 '10 at 21:42
  • 4
    Ignorance is sometimes a bliss. Turns out that facets are actually stored as attributes (with the advantage that you can declare UI components in it). Good to know. – BalusC Dec 06 '10 at 21:47
  • @BalusC: you're saying that facets are just attributes that can contain other JSF tags? That would make sense. Is that a PrimeFaces thing, or a JSF-wide fact? – Matt Ball Dec 06 '10 at 21:54
  • Facets have been in JSF since the beginning. E.g. `` for a ``. But they are not stored as attributes in standard JSF impl. I'm not sure how PrimeFaces does it, for that I would need to dig its source. – BalusC Dec 06 '10 at 22:01
9

You could declare a ui:param and let the template check the param while renderring.

The facet in the template could then be declared as:

<f:facet name="#{hideFooter == null or not hideFooter ? 'footer' : ''}">
     #{message}
</f:facet>

Any page can then declare this param

<ui:param name='hideFooter' value='#{some rule}' />

and set the appropriate rule for the param. For any page that does not declare the param, the footer will be displayed.

Adit
  • 103
  • 1
  • 3
7

Here's what I did in trying to conditionally render a facet within a composite component.

<composite:interface>
    <composite:facet name="header" required="false" />
</composite:interface>
<composite:implementation>
    <p:panel>
        <c:if test="#{empty component.facets.header}" >
            <f:facet id="#{cc.attrs.id}_default_header" name="header">
            all sorts of stuff here
            </f:facet>
        </c:if>
        <c:if test="#{not empty component.facets.header}">
            <composite:insertFacet id="#{cc.attrs.id}_custom_header" name="header" />
        </c:if>
        <composite:insertChildren id="#{cc.attrs.id}_content"/>
    </p:panel>
</composite:implementation>

This let's the user of the composite component supply the header facet if they want, and if they don't, we supply a default. Obviously, instead of providing a default, you could simply not do anything.

This mixes c:if in jsf controls, but we didn't see any adverse effects.

digitaljoel
  • 26,265
  • 15
  • 89
  • 115
  • 2
    interesting. I thought of that, but since I don't have an environment to test it, I was afraid that mixing `c:if` there would not behave as expected. Let's see if it works for the case of the OP. :) – Bozho Dec 06 '10 at 19:59
  • This did not work for me. @Bozho: see previous sentence. I think the problem is as @BalusC described in his comment on my question, above. – Matt Ball Dec 06 '10 at 20:10
  • Curious what you mean by "did not work for me" since we have had this code running in production for months. – digitaljoel Dec 06 '10 at 20:20
  • What I mean is: `#{message}` did not omit the footer when `#{message}` is empty. I'm not writing a composite component, so that might be part of the problem. – Matt Ball Dec 06 '10 at 20:24
  • @Bozho - the concern about c:if is obviously well founded. I believe f:facet is a tag handler and not a component, so it is evaluated the same time as c:if if I understand it correctly. – digitaljoel Dec 06 '10 at 20:27
  • @Matt yeah, I wondered how it would behave outside of the component. Sorry to hear it didn't work for you. I don't have a jsf environment anymore, so I can't look into that part further :( – digitaljoel Dec 06 '10 at 20:38
  • 4
    It's indeed only useful in composite components or tag files since they are basically instantiated/executed once per declared tag everytime. However, in a normal view you're dependent on more factors: JSTL for example won't behave as expected when the EL is depending on some JSF UIData/repeated variable since this information isn't available during view build time. – BalusC Dec 06 '10 at 21:18
  • @BalusC thanks for the great explanation. Much better put than my comment (as usual) – digitaljoel Dec 06 '10 at 21:28
2

I successfully solved this problem using ui:fragment

<ui:fragment rendered="...Test...">
<f:facet name="footer">
...
</f:facet>
</ui:fragment>

works for example to conditionnaly render the footer of a primefaces datatable (the rendered attribute of the facet does not work).

Ludovic Pénet
  • 1,136
  • 1
  • 16
  • 32
2

I have come across a similar issue with plain JSF. I am not sure how a <p:panel> is rendered, but if it is rendered as a table, you can try this:

First, declare a CSS-class like this:

.HideFooter tfoot {
    display: none;
}

Then set that class conditionally on the panel:

<p:panel styleClass="#{renderFooterCondition ? null : 'HideFooter'}">

The footer is still rendered in the JSF-sense, but it is not displayed and does not take up any space in the page when viewed by the user-agent.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
0

Not sure how well this would work for your footer, but I had the same issue with a legend I was trying to conditionally render. I fixed it by using the rendered on anything inside the facet tag.

<p:fieldset>
<f:facet name="legend">
    <h:outputText value="#{header1}" rendered="#{header1.exists}"/>
    <h:outputText value="#{header2}" rendered="#{not header1.exists}"/>
</f:facet>
content
</p:fieldset>

I had difficulty trying c:if with my ui:repeat, so this was my solution. Not quite the same as your problem, but similar.

eisbaer
  • 328
  • 1
  • 3
  • 10
0

Facets are not intended to render HTML, which is why it doesn't have the rendered attribute. Facets add functionality to a page. The term facet is probably a poor choice of name. It's very ambiguous.

..if the list compiled by ITworld's Phil Johnson has it right, the single hardest thing developers do is name things.

ie.

JSF facets

A project facet is a specific unit of functionality that you can add to a project when that functionality is required. When a project facet is added to a project, it can add natures, builders, classpath entries, and resources to a project, depending on the characteristics of the particular project. JSF facets define the characteristics of your JSF enabled web application. The JSF facets specify the requirements and constraints that apply to your JSF project.

The JSF facets supply a set behaviors and capabilities to your web application.

user1491819
  • 1,790
  • 15
  • 20
0

This is a counter-answer to the answer from Ludovic Pénet.

This worked for me in <f:facet name="footer"> in selected p:column items of a p:dataTable (Primefaces 5.3):

...

Note how I have the ui:fragment inside the f:facet, not outside (not wrapping) it. It definitely completely removes the entire row when every footer facet is tested to NOT render (as far as I can tell, independent of the content within the ui:fragment).

0

Try with this, from primefaces web page

<p:columnGroup type="footer">
    <p:row>
        <p:column colspan="3" style="text-align:right" footerText="Totals:" />
        <p:column footerText="your value in ajax" />

        <p:column footerText="your value in ajax" />
    </p:row>
</p:columnGroup>

clik here, to view primefaces' webpage

0

For those who landed here trying to hide the footer, instead of header, but the syntax component.facets.footer didn't work, should try this:

<p:panel id="panelContent">
    <c:if test="#{not empty cc.facets.footer}">
        <f:facet name="footer" height="100%">
           your content
        </f:facet>
    </c:if>
</panel>
Claudiomir
  • 109
  • 1
  • 7
-1

Why don't you enclose the content of the footer into a panelGroup which has the rendered attribute?

This way:

<p:panel header="some text">
    <f:facet name="footer">
    <h:panelGroup rendered="#{!empty message}">
        #{message}
    </h:panelGroup>
    </f:facet>
    <!-- ... -->
</p:panel>

I do it in my weapp and it works, no footer is rendered.

I don't use primefaces though, I do it with h:datatable, but I think that it must works with p:panel too.

choquero70
  • 4,470
  • 2
  • 28
  • 48
-1

I try this solution and ok. (http://www.coderanch.com/t/431222/JSF/java/dynamically-set-panel-header-condition)

<rich:dataGrid value="#{myMB.student.list}" rendered="#{!empty myMB.student and !empty myMB.student.list}" var="st" rowKeyVar="row">
    <rich:panel>                                            
        <f:facet name="header">
        <h:panelGroup id="panelHeader">
            <h:outputText value="Just one student" id="header1" required="true" rendered="#{!myMB.manyStudents}"/>
            <h:outputText value="#{row+1}º Student"  id="header2" required="true"  rendered="#{myMB.manyStudents}"/>
            </h:panelGroup>                                 
        </f:facet>
<rich:panel>