JSF composite component with own facet inside p:dataTable - jsf

I hava a composite component with a PrimeFaces dataTable inside. The column definition should be done outside of this component in a facet definition.
<composite:interface>
<composite:attribute name="domains" required="true"/>
<composite:facet name="domainColumns" required="true"/>
</composite:interface>
<composite:implementation>
<p:dataTable var="var" value="#{cc.attrs.domains}">
<composite:renderFacet name="domainColumns"/>
</p:dataTable>
</composite:implementation>
I want to use this component like shown here:
<my:domainTable domains="#{adminTsWorkspaceBean.domains}">
<f:facet name="domainColumns">
<p:column headerText="Header">
<h:outputText value="#{var.id}" />
</p:column>
</f:facet>
</my:domainTable>
Interestingly: If I use the p:column as a child and not as a facet and then in my component <composite:insertChildren/>, I have the desired output. But of course I can insert the children only at one position in my component. I'm trying to define the column for two different dataTables?

Related

How to include a primeface column into a composite

I have a primefaces datatable with a lot of similar columns such as
<p:column filterBy="#{element.value}" filterFunction="#{applicationHelperController.filterByNumber}" styleClass="ps-90-header" >
<f:facet name="header">
<p:tooltip for="#next" value="Some header tooltip"/>
<h:outputText id="#{header}_id" value="Some header" styleClass="ps-90-header" />
</f:facet>
<h:outputText value="#{element.value}" />
</p:column>
Is there a way of creating a composite for it such as to write
<mine:column value="#{element.value}" header="Some header" headerTooltip="Some header tooltip" />
I've been searching/trying things for a day now without any real success.
I've managed to use an <ui:include...> with params but it's almost as verbose as the original
<ui:include src="/WEB-INF/includes/countColumn.xhtml" >
<ui:param name="value" value="#{element.value}"/>
<ui:param name="header" value="Some header"/>
<ui:param name="headerTooltip" value="Some header tooltip"/>
</ui:include>
I've tried using taglib as preconized here How to create a composite component for a datatable column? even if it uses richfaces and this solution Column as Composite Component inside DataTable but none work.
I've also read somewhere that <p:column...> should be a direct child of <p:datatable...> so it is not possible but I believe that was with old primefaces version (3 or 4).
So, does anyone know if it is possible with Primefaces 6 or 8 and if so how?
You can use custom Facelet tags to DRY your code.
Simply create tags for columns like:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<p:column ...>
...
</p:column>
</ui:composition>
This is how a table looks in one of our projects:
<dt:dataTable controller="#{controller}"
sortField="activationDate">
<dt:column id="id"/>
<dt:columnDate id="activationDate"/>
...
<dt:dataTable/>

How pass a converter into a composite component with p:inputText?

I want to put a filter of a p:datatable into a composite component. The filter use the p:inputText tag and an p:ajax tag to define a typing delay. The problem is, that the converter inside the composite component do nothing. When I use javax.faces.Long, the filter is always String.
The working code, that I want to use in a facelet:
<p:column headerText="colum title"
filterBy="#{p.p.idx}" sortBy="#{p.p.idx}">
<f:facet name="filter">
<p:inputText id="indexFilterInput"
converter="javax.faces.Long" class="ui-column-filter">
<p:ajax event="keyup" delay="800"
oncomplete="PF('portDT').filter()" />
</p:inputText>
</f:facet>
<h:outputText value="#{portRow.portIdx}" />
</p:column>
I put the f:facet into the composite component:
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:cc="http://xmlns.jcp.org/jsf/composite">
<cc:interface>
<cc:attribute name="widgetVar" />
<cc:attribute name="delay" />
<cc:attribute name="converter" />
<cc:insertFacet name="filter" />
</cc:interface>
<cc:implementation>
<f:facet name="filter">
<p:inputText id="filterInput" converter="#{cc.attrs.converter}"
class="ui-column-filter" type="search">
<p:ajax event="keyup" delay="#{cc.attrs.delay}"
oncomplete="PF('#{cc.attrs.widgetVar}').filter()" />
</p:inputText>
</f:facet>
</cc:implementation>
</ui:component>
Here is the call of the component gf:filterField:
<p:column style="width:4em"
headerText="#{lbl['general.column.id']}" exportable="true"
filterBy="#{p.p.id}" sortBy="#{p.p.id}">
<gf:filterField id="index" widgetVar="portDT" delay="800" converter="javax.faces.Long"/>
<h:outputText value="#{portRow.portId}" />
</p:column>
When change the converter in the p:inputText inside the composite component, the converter is also ignored. I need to pass the converter as a parameter, because some columns are String columns and some are Long.
Is there any other tag in the cc:interface as cc:attribute?

p:dialog in composite component JS error: Uncaught TypeError: Cannot read property 'show' of undefined

I am trying to create a composite component to be used in my JSF 2.2 pages. Here is the composite:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:cc="http://java.sun.com/jsf/composite">
<cc:interface componentType="searchUser">
<!-- Define component attributes here -->
<cc:attribute name="update" type="java.lang.String"
shortDescription="the component to update"/>
<cc:attribute name="widgetVar" type="java.lang.String"/>
</cc:interface>
<cc:implementation>
<p:dialog header="Search for User"
widgetVar="#{cc.widgetVar}"
modal="true"
id="searchDialog"
>
<p:panelGrid columns="2" id="searchPanel">
<p:outputLabel value="First Name: "
for="firstname"
/>
<p:inputText id="firstname" value="#{SearchBean.firstname}"/>
<p:outputLabel value="Last Name: "
for="lastname"
/>
<p:inputText id="lastname" value="#{SearchBean.lastname}"/>
<p:commandButton value="search"/>
<p:dataTable value="#{SearchBean.results}" var="user">
<p:column headerText="First Name">
#{user.firstName}
</p:column>
<p:column headerText="Last Name">
#{user.lastName}
</p:column>
<p:column headerText="E-Mail">
#{user.email}
</p:column>
</p:dataTable>
</p:panelGrid>
</p:dialog>
</cc:implementation>
and I am using it like:
<my:searchUser update="foo" id="searchDLG" widgetVar="sdgl"/>
<p:commandButton onclick="PF('sdgl').show();" type="button" value="search for user" />
Currently my FacesComponent Java is empty:
#FacesComponent("searchUser")
public class searchUser extends UIInput implements NamingContainer {
}
The target is to create a search form /dialog, wich I can use in multiple locations.
My problem is, that on the click i get the error: Uncaught TypeError: Cannot read property 'show' of undefined.
Any advice on what I am missing would greatly be appreciated.
This is not the right way to reference a composite attribute.
<p:dialog widgetVar="#{cc.widgetVar}">
It's basically referencing a property of the backing component itself.
Composite attributes are available by #{cc.attrs} map.
<p:dialog widgetVar="#{cc.attrs.widgetVar}">
The JS error is caused because the actual widgetVar value is empty and the PF('sdgl') returned undefined on which you're then blindly trying to invoke the show() function. It's basically like a NullPointerException in Java.
See also:
Our composite component wiki page
Java EE 7 tutorial - Composite Components
Unrelated to the concrete problem, there are 2 other problems with this composite:
The backing component doesn't return the required component family of UINamingContainer.COMPONENT_FAMILY. You can fix it by extending from UINamingContainer instead of UIInput, or by overriding the getFamily() method to return UINamingContainer.COMPONENT_FAMILY.
This is not the right purpose of a composite component. There's no single model value at all. I.e. you don't have a <my:searchUser value="..."> analogous with existing standard components. Rather use an include or tagfile. See also When to use <ui:include>, tag files, composite components and/or custom components? Otherwise, rework the composite as such that you can ultimately use <my:searchUser value="#{bean.foundUser}">.

JSF composite components. How do I pass top-level f:facet to underlying component?

For a long time I had one composite component defined and used as follows:
<!-- simple -->
<composite:interface>
<composite:facet name="header"/>
</composite:interface>
<composite:implementation>
<composite:renderFacet name="header"/>
</composite:implementation>
<xyz:simple>
<f:facet name="header">
test
</f:facet>
</xyz:simple>
Now I want to reuse it in another component and pass it's facet value to underlying component:
<!-- advanced -->
<composite:interface>
<composite:facet name="header"/>
</composite:interface>
<composite:implementation>
<!-- some stuff -->
<xyz:helper>
<composite:insertFacet name="header"/>
</xyz:helper>
</composite:implementation>
<xyz:advanced>
<f:facet name="header">
test 2
</f:facet>
</xyz:advanced>
And I get no output. Any ideas?

a composite component inside panelgrid is not 'expanded'

Basically, i've been using this pattern again and again inside a panelgrid :
<h:panelGrid columns="2" >
<h:outputLabel for="heroName" value="Hero Name : " />
<h:panelGroup>
<h:inputText label="Hero Name : "
value="#{ccBean.data.map['heroName']}" id="heroName" />
<p:message for="heroName" />
</h:panelGroup>
...
</h:panelGrid>
I notice that i duplicate things so much, like the id in h:inputText, which will be duplicated again in for="idValue" for outputLabel, and then again in p:message. The same thing happens with the inputText's label value and h:outputLabel value.
So, i was thinking about making a simple composite component to wrap these up :
<composite:interface>
<composite:attribute name="id" required="true" />
<composite:attribute name="label" required="true" />
<composite:attribute name="value" required="true" />
<composite:attribute name="includeLabel" required="false" default="false"/>
</composite:interface>
<composite:implementation>
<h:outputLabel for="#{cc.attrs.id}" value="#{cc.attrs.label}"
render="#{cc.attrs.includeLabel}" />
<h:panelGroup>
<h:inputText label="#{cc.attrs.label}"
value="#{cc.attrs.value}" id="#{cc.attrs.id}" />
<p:message for="#{cc.attrs.id}" />
</h:panelGroup>
</composite:implementation>
And then i make use of the composite components :
<h:panelGrid columns="2" >
<s:inputText
id="heroName"
label="Hero Name : "
value="#{ccBean.data.map['heroName']}"
includeLabel="true"
/>
<s:inputText
id="heroName2"
label="Hero Name 2 : "
value="#{ccBean.data.map['heroName2']}"
includeLabel="true"
/>
...
</h:panelGrid>
That composite component works fine, but the panelGrid now assumes that the s:inputText is one component. Before using the composite component, there are 2 components in one row, which is the outputLabel and the panelGroup.
Is it possible to hint the composite component to 'expand' itself in this case so that it takes up 2 columns instead of 1 column in my panelGrid ?
Thank you !
No, unfortunately not. Your best bet is to create a simple tag file instead.
See also:
How to make a grid of JSF composite component?

Resources