12

Given this datatable (naturally working):

<rich:dataTable var="var" value="#{values}">
<rich:column>
  <f:facet name="header">
   HEADER
  </f:facet>
  <h:outputText value="#{var}" />
</rich:column>
</rich:dataTable>

If I define a custom component (also ok in the syntax and at the right place under resources/components):

   <?xml version="1.0" encoding="UTF-8"?>
    <html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:a4j="http://richfaces.org/a4j"
        xmlns:rich="http://richfaces.org/rich"
        xmlns:composite="http://java.sun.com/jsf/composite">

    <!-- INTERFACE -->
    <composite:interface>
        <composite:attribute name="val" />
    </composite:interface>

    <!-- IMPLEMENTATION -->
    <composite:implementation>
        <rich:column>
            <f:facet name="header">
       HEADER
       </f:facet>
       <h:outputText value="#{cc.attrs.val}" />
       </rich:column>
    </composite:implementation>
    </html>

Why does the following does not work?

<?xml version="1.0" encoding="UTF-8"?>
<ui:composition template="/WEB-INF/templates/default.xhtml"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:a4j="http://richfaces.org/a4j"
    xmlns:rich="http://richfaces.org/rich"
    xmlns:my="http://java.sun.com/jsf/composite/components">
    <ui:define name="content">
        <rich:dataTable var="var" value="#{values}">
                 <my:mycolumn val="#{var}"/>
                </rich:dataTable>
    </ui:define>
</ui:composition>

Do you know how could I let it work (I want to define my own column and save code)? Thanks a lot! Bye

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Federico
  • 561
  • 2
  • 11
  • 32

2 Answers2

26

The <my:mycolumn> element must be an instance of UIColumn as that's the only valid child of a UIData component during the render response phase. All other UIComponent types will be ignored, thus not rendered. A composite component is implicitly a UINamingContaner component, which isn't a UIColumn and therefore ignored.

A PrimeFaces <p:dataTable> with a backing component that extends UIColumn also won't work due to the wrong lifecycle of a composite component. The column has to be created during the view build time, while the composite component's body is created during view render time.

The solution is to create a tag file instead, which means an extra .taglib.xml file, yet it works flawlessly.

/WEB-INF/tags/column.xhtml:

<ui:composition
    xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets" 
    xmlns:rich="http://richfaces.org/rich">
    <rich:column>
        <f:facet name="header">HEADER</f:facet>
        <h:outputText value="#{val}" />
    </rich:column>
</ui:composition>

/WEB-INF/my.taglib.xml:

<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib 
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
    version="2.0">
    <namespace>http://example.com/my</namespace>
    <tag>
        <tag-name>column</tag-name>
        <source>tags/column.xhtml</source>
        <attribute>
            <description>Column value</description>
            <name>val</name>
        </attribute>
    </tag>
</facelet-taglib>

Note: The <attribute> entries are not mandatory, but are nice for documentation purposes, such as generated docs and IDE autocomplete.

/WEB-INF/web.xml:

<context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/WEB-INF/my.taglib.xml</param-value>
</context-param>

Usage:

<ui:composition
    xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets" 
    xmlns:rich="http://richfaces.org/rich"
    xmlns:my="http://example.com/my">
    <rich:dataTable value="#{values}" var="value">
        <my:column val="#{value}" />
    </rich:dataTable>
</ui:composition>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks **a lot** BalusC. But with that method you can't add children with some `` can you ? Is there a way to use `jsf/composite` tags and say the root UIComponent must be `javax.faces.component.UIColumn` ? – Anthony O. Sep 26 '12 at 12:25
  • 1
    @Anthony: Just use ``. No, there's no way with composites, as already answered. – BalusC Sep 26 '12 at 12:26
  • 2
    This workaround seems like a huge oversight in the JSF specification. The above solution works for me as well; however, how would I emulate a cc:renderFacet with a newly defined facet? Ui:define/ui:insert with a name works for me. But, is there a way to still provide another f:facet child and use some equivalent of cc:renderFacet in column.xhtml? – GreenieMeanie May 12 '14 at 20:57
  • It is all good and fine, however, why do I need to write a "my.tablib.xml" and declare it in web.xml while this sort of mechanics is not necessary defining composites?? – wh81752 Mar 28 '15 at 09:04
  • I'm also wondering if there is some possibility to include facets like with `` in composite components. I couldn't find anything. Anybody any idea? – Martin Höller Jul 06 '18 at 06:38
  • 1
    @MartinHöller In your definition of ``column.xhtml`` you can use ```` to define where child tags will be put. It appears that you also can define named insertion points just like with templates. I tested adding ```` into ``column.xhtml``, and referred to it from the outside using ``hello quasi facet`` - this appears to work, even in combination with an unnamed ```` for direct children. – Antares42 Oct 12 '18 at 11:30
  • @whaefelinger - for historic reasons. Taglibs are older than composite components and have a more manual setup. On the other hand, they come with far less overhead on the server side, being fairly simple templating as opposed to cc's naming container magic. – Antares42 Oct 12 '18 at 11:36
  • @Antares42 I employed your advice and modified it like , this also worked, the commandLink and its action linked, without this method the link will just direct me to error page. doesn't work in template file if it is placed inside the custom tag as a children of the tag, but is available in that way. Using and , and looks having no difference. – Aaron Chen Jan 04 '21 at 15:02
-1

I ran into the same question (primefaces), my compromise: exclude the column tag from the composite

<rich:dataTable var="var" value="#{values}">
    <rich:column rendered=#{yesOrNo}">
       <my:mycolumn val="#{var}">
       .. with child nodes if you want ...
       </my:mycolumn>
    </rich:column>
</rich:dataTable>
steve
  • 615
  • 6
  • 14
  • This Is A Very weird and illogical answer If contains code that is factually not correct. And you did not exclude a column but include it, weird. – Kukeltje Mar 21 '20 at 08:07
  • The OP's composite contained the rich:column tag itself. This compromise works, because the is outside of the composite (thats why it can be created during the view build time, while the composite component's body is created during view render time, as BalusC told) If the composite contains a big code (as in my case), this could help. Syntax: it uses the variables and structure from the question. – steve Mar 22 '20 at 10:20