Primefaces Dynamic columns not working with lazy loading - jsf-2

I am using PF 5.0, JSF 2.0 (Mojarra), Spring Webflow 2.3. I am facing multiple issues with the Datatable Lazy loading.
a.) When I try to have Multiple Select for Columns with Lazy Data Model for the DataTable component, When I enable single select for Data table , it works fine .
b.) Another issue that I am facing with the Datatable is that I am unable to load dynamic columns with Lazy Data table , It used to work fine for the non-Lazy version.
Any suggestions to resolve these issues are welcome. Please find the code below.
<p:dataTable id="docsTable" var="document"
value="#{datasource}" lazy="true" paginator="true" dynamic="true"
rows="#{searchActionBean.maxRows}" paginatorTemplate="{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="10,25,50,100" rowKey="#{document.ID}"
sortMode="single" resizableColumns="true" scrollable="true" scrollHeight="350" >
<f:facet name="header">
<p:toolbar>
<f:facet name="left">
<p:commandButton type="button" title="Print" icon="ui-icon-print" />
<span class="ui-separator">
<span class="ui-icon ui-icon-grip-dotted-vertical" />
</span>
<p:commandButton id="reptoggler" value="Reports" action="reportsAction" icon="ui-icon-note">
<f:param name="stdReportName" value="reportsMain" />
</p:commandButton>
</f:facet>
<f:facet name="right">
<h:outputText value="#{searchActionBean.domainName}" />(<h:outputText value="#{searchActionBean.resultCount}" />)
<span class="ui-separator">
<span class="ui-icon ui-icon-grip-dotted-vertical" />
</span>
<h:commandLink>
<img src="${request.contextPath}/images/excel.png" width="15" height="15" />
<p:dataExporter type="xls" target="docsTable" fileName="documents" />
</h:commandLink>
<span class="ui-separator">
<span class="ui-icon ui-icon-grip-dotted-vertical" />
</span>
<h:commandLink>
<img src="${request.contextPath}/images/pdf.png" width="15" height="15"/>
<p:dataExporter type="pdf" target="docsTable" fileName="documents" />
</h:commandLink>
<span class="ui-separator">
<span class="ui-icon ui-icon-grip-dotted-vertical" />
</span>
<p:commandButton id="toggler" type="button" value="Columns" icon="ui-icon-calculator" />
<p:columnToggler datasource="docsTable" trigger="toggler" />
</f:facet>
</p:toolbar>
</f:facet>
<p:column selectionMode="multiple" style="text-align:center; width:25px">
<f:facet name="header">
<h:outputText value="Select" />
</f:facet>
</p:column>
<p:columns value="#{searchResultsColumns}" var="column" styleClass="wrap" width="#{column.width}" columnIndexVar="colIndex" sortBy="#{document[column.pfResColumnName]}" filterBy="#{document[column.pfResColumnName]}">
<f:facet name="header">
<h:outputText value="#{column.displayResultColumnName}" />
</f:facet>
<h:outputText value="#{document[column.pfResColumnName]}" rendered="#{column.resColumnType eq 'DATE'}">
<f:convertDateTime pattern="MM/dd/yyyy" />
</h:outputText>
<h:outputText value="#{document[column.pfResColumnName]}" rendered="#{column.resColumnType eq null}" />
<h:outputText value="#{document[column.pfResColumnName]}" rendered="#{column.resColumnType eq 'SPL'}">
<f:converter converterId="gov.nih.nci.ctep.idart.converters.SplConverter"></f:converter>
</h:outputText>
</p:columns>
</p:dataTable>
And the Bean code, I have verified that the Collections searchResultColumns is not empty.
public class SearchActionBean extends LazyDataModel<DocumentResultDTO> implements Serializable
{
private List<DocumentResultDTO> datasource;
#Override
public DocumentResultDTO getRowData(String rowKey) {
for (DocumentResultDTO doc : datasource) {
if (doc.getID().equals(rowKey))
return doc;
}
return null;
}
#Override
public Object getRowKey(DocumentResultDTO doc) {
return doc.getID();
}
#Override
public List<DocumentResultDTO> load(int first, int pageSize, String sortField,
SortOrder sortOrder, Map<String, Object> filters) {
datasource = new ArrayList<DocumentResultDTO>();
/*this.execute();*/
commonQueriesLocal = ejbService.getCommonQueriesLocal();
srchDelegate = new SearchDelegate();
super.setSearchDelegate(srchDelegate);
try
{
//Set the visibility of all the columns depending on the JQuery table visibility setting from the client side
String invisibleColNames = request.getParameter("invisibleColNames");
ArrayList<UserResultColumnDTO> dispCols = getDisplayColumns();
ApplicationUtils.markVisibilityForColumns(dispCols,invisibleColNames);
int iSortColumnIndex = 1;
String sSortDirection = sortOrder.toString().equalsIgnoreCase("ASCENDING") ? "asc" : "desc";
if(request.getParameter("iSortCol_0") != null)
iSortColumnIndex = Integer.parseInt(request.getParameter("iSortCol_0"));
Query query =
(Query) request.getSession().getAttribute(IdartWebGlobalConstants.USER_QUERY);
if(request.getSession().getAttribute("executeSavedQuery") != null &&
request.getSession().getAttribute("executeSavedQuery").toString().equalsIgnoreCase("true")
)
pageSize = query.getUserQuery().getPageCount();
else
query.getUserQuery().setPageCount(pageSize);
query.getUserQuery().setSortOrder(sSortDirection);
setDataSortColumn(iSortColumnIndex, getDomainNameToSetSortColumn(query.getName()), query);
performNavigation(pageSize, first);
// Save the query object in session
if(getDomainName().equalsIgnoreCase("DOCUMENTS"))
{
setSearchResults((ArrayList)request.getAttribute("documentResults"));
setSearchResultsColumns(dispCols);
}
System.out.println("search Result "+searchResults.size());
System.out.println("search Columns "+dispCols.size());
// process on filters to be displayed
customizeFilterResults();
}
catch(Exception ex)
{
log.error("-- GetData Method Error "
+ ex.getMessage()
+ " --", ex);
}
datasource= getSearchResults();
System.out.println("first "+first+" pageSize "+pageSize+" sortField "+sortField+" sortOrder "+sortOrder);
System.out.println("searchResultsColumns"+datasource);
System.out.println("searchResultsColumns "+searchResultsColumns);
System.out.println("GET searchResultsColumns "+getSearchResultsColumns());
// rowCount
int dataSize = datasource.size();
this.setRowCount(Integer.parseInt(getResultCount()));
return datasource;
}
}

Look at the console and see if you see any errors. Lazy Loading in early versions of PrimeFaces (2.0) seems to break certain PrimeFaces tooling (like ToolTips).
I was able to fix this by importing JQuery UI after loading the lazy loaded section (i.e inside the <tab> of a lazy loaded <tabView>):
<script src='${request.contextPath}/components/jquery/jquery-ui.js'></script>

Related

Initializing primefaces datatable rowkey

I have a primefaces datatable that I'm populating with properties of objects in an ArrayList once the user clicks a search button. I need to add a column with a radio button so that the user can select a row of the table to update. I'd like to set the rowkey to be the metadataKey property of my objects, but I get the following error:
rowKey="#{result.metadataKey}": Property 'metadataKey' not found on
type java.lang.String
I believed that this was caused by the metadataKey not existing at the time the table is rendered. To remedy it, I set the table to render only after the user had completed their search, but this resulted in the same error.
Here is the relevant html:
<h:form id="dataForm">
<p:panel styleClass="centered">
<p:messages id="message" showDetails="true" autoUpdate="true" />
<p:outputLabel id="searchLabel" value="Search for Metadata: " />
<p:inputText id="search"
value="#{updateMetadataItemsBean.searchTerm}" />
<p:commandButton id="searchButton" value="Search"
action="#{updateMetadataItemsBean.search}"
update="dataForm:metadataitems, message, dataForm:dataPanel" />
<br />
<br />
<br />
</p:panel>
<p:panel styleClass="center" id="dataPanel" >
<p:dataTable id="metadataitems" var="result"
value="#{updateMetadataItemsBean.results}" styleClass="center"
resizableColumns="true"
selection="updateMetadataItemsBean.selectedMetadataItem"
rowKey="#{result.metadataKey}" rendered="#{updateMetadataItemsBean.show}">
<p:column headerText="Select Item to Update" selectionMode="single"></p:column>
<p:column headerText="Metadata Key">
<h:outputText value="#{result.metadataKey}" />
</p:column>
<p:column headerText="Map Name">
<h:outputText value="#{result.mapName}" />
</p:column>
<p:column headerText="Metadata Name">
<h:outputText value="#{result.metadataName}" />
</p:column>
<p:column headerText="Metadata Value">
<h:outputText value="#{result.metadataValue}" />
</p:column>
<p:column headerText="WTX Adapter">
<h:outputText value="#{result.wtxAdapter}" />
</p:column>
<p:column headerText="Card Number">
<h:outputText value="#{result.cardNumber}" />
</p:column>
<p:column headerText="Create Date TS">
<h:outputText value="#{result.createDateTS}" />
</p:column>
</p:dataTable>
<br />
<br />
<br />
</p:panel>
This is the relevant code from the bean:
public class UpdateMetadataItemsBean implements Serializable{
public static final long serialVersionUID = 4;
private String metadataKey;
private String mapName;
private String metadataName;
private String metadataValue;
private String wtxAdapter;
private String cardNumber;
private String createDateTS;
private String searchTerm;
private String selectedDataKey;
private boolean show = false;
private MetadataItems selectedMetadataItem;
private ArrayList<MetadataItems> results = new ArrayList<MetadataItems>();
[getters and setters]
public void search() {
Search s = new Search();
setResults(s.search(searchTerm));
setShow(true);
}
Thanks in advance to anyone who can point me in the right direction.
Alter
selection="updateMetadataItemsBean.selectedMetadataItem"
to
selection="#{updateMetadataItemsBean.selectedMetadataItem}".

Unable to update component using primefaces update attribute

I am new to JSF and primefaces.I want to updated data grid when file upload completes. i tired update attribute but it did not update component. i could see updated data grid only after reloading the page.
<h:form>
<p:fileUpload fileUploadListener="#{fileUploadView.handleFileUpload}" mode="advanced" dragDropSupport="false"
multiple="true" update=":content:images" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" />
<p:growl id="messages" showDetail="true" />
</h:form>
<h:form id="content">
<p:dataGrid var="image" value="#{dataGridView.images}" columns="2"
rows="12" paginator="true" id="images" rendered="#{not empty dataGridView.images}"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="6,12,16" >
<f:facet name="header">
Your Album
</f:facet>
<p:panel header="#{image.imageName}" style="text-align:center;">
<h:panelGrid columns="1" style="width:100%">
<image src="#{image.imageLocation}" style="height: 10%;width:30%;"/>
</h:panelGrid>
</p:panel>
</p:dataGrid>
</h:form>
handleFileUpload method
public void handleFileUpload(FileUploadEvent event) {
currentSession.setAttribute("username","sravanyahoo#gmail.com");
String userName = (String) currentSession.getAttribute("username");
String realPath = ctx.getRealPath("/");
String contextPath = ctx.getContextPath();
try{
UploadedFile uploadedFile = event.getFile();
String imageLocation = ImageUtils.uploadFile(realPath,contextPath,uploadedFile,userName);
}
catch(Exception e)
{
}
helper.updateImageList(userName,dataGridView);
FacesMessage message = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, message);
}
updateImageList method
public void updateImageList(String userName,DataGridView view){
view.setImages(getImagesFromDB(userName));
}
Most likely your dataGrid is not rendered before the upload and update (rendered="#{not empty dataGridView.images}". If it is indeed not, you are running into one of jsf bascis that you cannot update a component if it is not rendered.

I can't delete a row from datatable primefaces

I know this ask is repeated, but none response help me to solve this problem.
I'm trying to delete a row from a primefaces datatable with a commandButton and a confirmDialog, this doesn't work because the backBean isn't getting the values. When I'm trying to delete this show me a null pointer exception. well see my code if you can help me.
This is my XHTML:
<h:form id="principalForm">
<p:menubar>
<p:menuitem value="Nuevo" icon="ui-icon-plusthick" oncomplete="PF('wdlgIngresar').show();"/>
</p:menubar>
<p:dataTable id="dtpacientes" value="#{beanPersona.listPersona}" var="per"
paginator="true" rows="15" rowsPerPageTemplate="15,20,30" paginatorPosition="bottom"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
selectionMode="single" selection="#{beanPersona.currPersona}" rowKey="#{per.id}">
<p:ajax event="rowSelect" listener="#{beanPersona.onRowSelect}" update=":dlgVer" oncomplete="PF('wdlgVer').show()" />
<p:column headerText="Id" sortBy="#{per.id}" filterBy="#{per.id}">
<h:outputText value="#{per.id}" />
</p:column>
<p:column headerText="Nombre" sortBy="#{per.nombres} #{per.apellidos}" filterBy="#{per.nombres} #{per.apellidos}">
<h:outputText value="#{per.nombres} #{per.apellidos}" />
</p:column>
<p:column headerText="Correo" sortBy="#{per.correo}" filterBy="#{per.correo}">
<h:outputText value="#{per.correo}" />
</p:column>
<p:column headerText="Edad" >
<h:outputText value="#{beanPersona.getEdad(per.nacimiento)}" />
</p:column>
<p:column headerText="Sexo" >
<h:outputText value="#{beanPersona.getSexo(per.sexo)}" />
</p:column>
<p:column id="actions" headerText="Acciones">
<p:commandButton title="Borrar" icon="ui-icon-trash" update=":principalForm:deleteConfirmDialog"
oncomplete="PF('deleteConfirmation').show();" actionListener="#{beanPersona.nullMethod}">
<f:setPropertyActionListener value="#{per}" target="#{beanPersona.currPersona}" />
</p:commandButton>
</p:column>
</p:dataTable>
<!-- HERE beanPersona.currPersona.id HAVE THE CURRENT VALUE CORRECTLY-->
<p:confirmDialog id="deleteConfirmDialog" header="Atencion!" severity="alert" widgetVar="deleteConfirmation"
message="Desea eliminar paciente Nº #{beanPersona.currPersona.id} ?" >
<p:commandButton id="confirm" value="Sí" oncomplete="PF('deleteConfirmation').hide();"
update=":principalForm:dtpacientes" action="#{beanPersona.eliminar(beanPersona.currPersona)}" />
<p:commandButton id="decline" value="No" oncomplete="PF('deleteConfirmation').hide();"/>
</p:confirmDialog>
</h:form>
This is my Bean:
#ManagedBean
#ViewScoped
public class BeanPersona implements Serializable {
private Persona persona = new Persona();
private Persona currPersona = new Persona();
public BeanPersona() {
persona = new Persona();
currPersona = new Persona();
}
public Persona getCurrPersona() {
return currPersona;
}
public void setCurrPersona(Persona currPersona) {
this.currPersona = currPersona;
}
public Persona getPersona() {
return persona;
}
public void setPersona(Persona persona) {
this.persona = persona;
}
public void eliminar(Persona currPersona) {
if(currPersona != null) {
new Dml().delete(currPersona, currPersona.getId());
listar();
System.out.println("succefull delete !!!!");
} else if(this.currPersona != null){
new Dml().delete(this.currPersona, this.currPersona.getId());
this.currPersona = new Persona();
listar();
System.out.println("succefull delete this.currPersona!!!!");
} else {
System.out.println("currPersona is null !!!!");
}
}
public String nullMethod () {return null;}
}
currPersona still null when I throw the method
The attribute onclick is for client side behaviour like executing some JS or showing up a dialog etc. You are not defining an actionListener for your commandButton at all.
Try to remove the onclick completely and use a proper actionListener instead:
<p:column id="actions" headerText="Acciones">
<p:commandButton title="Borrar" icon="ui-icon-trash"
update=":principalForm:deleteConfirmDialog"
oncomplete="PF('deleteConfirmation').show();"
actionListener="#{beanPersona.someActionListener}">
<f:setPropertyActionListener value="#{per}"
target="#{beanPersona.currPersona}" />
</p:commandButton>
</p:column>
I solved it, I just put process tag into "confirm" commandButton with value at "#this"
<p:column id="actions" headerText="Acciones">
<p:commandButton title="Borrar" icon="ui-icon-trash" update=":principalForm:deleteConfirmDialog"
oncomplete="PF('deleteConfirmation').show();" actionListener="#{beanPersona.setCurrPersona(per)}">
<f:setPropertyActionListener value="#{per}" target="#{beanPersona.currPersona}" />
</p:commandButton>
</p:column>
</p:dataTable>
<!-- HERE beanPersona.currPersona.id HAVE THE CURRENT VALUE CORRECTLY-->
<p:confirmDialog id="deleteConfirmDialog" header="Atencion!" severity="alert" widgetVar="deleteConfirmation"
message="Desea eliminar a paciente Nº #{beanPersona.currPersona.id} ?" closeOnEscape="true" >
<p:commandButton id="confirm" value="Sí" oncomplete="PF('deleteConfirmation').hide();"
update=":principalForm:dtpacientes" action="#{beanPersona.eliminar}" process="#this"/>
<p:commandButton id="decline" value="No" oncomplete="PF('deleteConfirmation').hide();"/>
</p:confirmDialog>

Backing bean method is getting null from inputs

This project is made with Primefaces 3.5, JSF 2.0, Oracle database.
The view:
<h:form id="schedule" class="schedule">
// more JSF and forms and dialogs
<p:dialog widgetVar="patientDialog" dynamic="true" header="Find Patient" showEffect="clip" hideEffect="explode" modal="true" >
<h:form id="frmPatient">
<h:panelGrid id="findPatient" columns="1">
<h:panelGrid id="findPatientParameters" columns="2">
<h:outputLabel value="Firstname: " />
<p:inputText id="name" label="name" size="15" value="#{waitinglistBean.name}" />
<h:outputLabel value="Patient Code: " />
<p:inputText id="pcode" label="pcode" size="15" value="#{waitinglistBean.pcode}" />
<br />
<p:commandButton value="Search" process="name" actionListener="#{waitinglistBean.patients}" update="patients"/>
</h:panelGrid>
<h:panelGrid id="findPatientDetails" columns="1">
<p:dataTable id="patients" var="patient" resizableColumns="true" scrollable="true" scrollWidth="1250"
scrollHeight="150" value="#{waitinglistBean.patients}"
rowKey="#{patient.PCode}" selection="#{waitinglistBean.selectedPatient}"
selectionMode="single" >
<f:facet name="header">
Click "Select" button after selecting a row to select patient.
</f:facet>
<p:column width="150" headerText="Code">
#{patient.PCode}
</p:column>
<p:column width="150" headerText="Family Name">
#{patient.PLname}
</p:column>
<p:column width="150" headerText="First Name">
#{patient.PFname}
</p:column>
<p:column headerText="Sex" >
#{patient.PSex}
</p:column>
<p:column headerText="Birthdate">
#{patient.PBorn}
</p:column>
<p:column headerText="Street">
#{patient.PStreet}
</p:column>
<f:facet name="footer">
<p:commandButton id="viewButton" value="Select" icon="ui-icon-search"
oncomplete="patientDialog.hide()"
update=":frmDialogs:frmWaiting:tabView:patientDetails"
process="patients"/>
</f:facet>
</p:dataTable>
</h:panelGrid>
</h:panelGrid>
</h:form>
</p:dialog>
<p:dialog widgetVar="patientDialog" dynamic="true" header="Find Patient" showEffect="clip" hideEffect="explode" modal="true" >
<h:form id="frmPatient">
<h:panelGrid id="findPatient" columns="1">
<h:panelGrid id="findPatientParameters" columns="2">
<h:outputLabel value="Firstname: " />
<p:inputText id="name" label="name" size="15" value="#{waitinglistBean.name}" />
<h:outputLabel value="Patient Code: " />
<p:inputText id="pcode" label="pcode" size="15" value="#{waitinglistBean.pcode}" />
<br />
<p:commandButton value="Search" process="name" actionListener="#{waitinglistBean.patients}" update="patients"/>
</h:panelGrid>
<h:panelGrid id="findPatientDetails" columns="1">
<p:dataTable id="patients" var="patient" resizableColumns="true" scrollable="true" scrollWidth="1250"
scrollHeight="150" value="#{waitinglistBean.patients}"
rowKey="#{patient.PCode}" selection="#{waitinglistBean.selectedPatient}"
selectionMode="single" >
<f:facet name="header">
Click "Select" button after selecting a row to select patient.
</f:facet>
<p:column width="150" headerText="Code">
#{patient.PCode}
</p:column>
<p:column width="150" headerText="Family Name">
#{patient.PLname}
</p:column>
<p:column width="150" headerText="First Name">
#{patient.PFname}
</p:column>
<p:column headerText="Sex" >
#{patient.PSex}
</p:column>
<p:column headerText="Birthdate">
#{patient.PBorn}
</p:column>
<p:column headerText="Street">
#{patient.PStreet}
</p:column>
<f:facet name="footer">
<p:commandButton id="viewButton" value="Select" icon="ui-icon-search"
oncomplete="patientDialog.hide()"
update=":frmDialogs:frmWaiting:tabView:patientDetails"
process="patients"/>
</f:facet>
</p:dataTable>
</h:panelGrid>
</h:panelGrid>
</h:form>
</p:dialog>
// more JSF and dialogshizzle
</h:form>
The backing bean:
#ManagedBean(name="waitinglistBean")
#ViewScoped
public class WaitinglistBean implements Serializable
{
private Patients patient;
private OrWaitinglist orWaitinglist;
private List<Patients> patients = new ArrayList<Patients>();
private String name;
private Integer pcode;
public WaitinglistBean() {
}
public void setSelectedPatient(Patients patient) {
this.patient = patient;
}
public Patients getSelectedPatient() {
return patient;
}
public OrWaitinglist getOrWaitinglist() {
return orWaitinglist;
}
public void setOrWaitinglist(OrWaitinglist orWaitinglist) {
this.orWaitinglist = orWaitinglist;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPcode() {
return pcode;
}
public void setPcode(Integer pcode) {
this.pcode = pcode;
}
public List<Patients> getPatients() {
PatientsDao patientsDao = new PatientsDaoImpl();
patients = patientsDao.getSearchPatient(name, pcode);
System.out.println(name + " " + pcode);
return patients;
}
public void createOrWaitinglist()
{
orWaitinglist.setPatients(patient);
}
So first a user enter a string: "Firstname" and an Integer: "Patient Code".
When he hits the "Search" button it fires the actionListener="#{patientBean.patients}
inside that commandbutton. The method needs 2 parameter, name and pcode which have their own setters/getters.
When I look in console it shows me the printout of these 2 parameters, and they are twice "null", so that means the parameters from the JSF pages were not passed to the backing bean, although the value of the inputText has been set to the backingbean set method: value="#{patientBean.name}"
My question now is why do I get these null-values. I've read several posts which use these setters, but still I get null-values.
after edit i still have the same problem;
for send value of input component in h:panelgrid to the backingbean use somthing like this:
<p:commandButton value="Search" process="#(frmPatient:findPatientParameters :input)"
actionListener="#{patientBean.patients}" update="patients"/>

Binding a managed bean instance to composite component

I have a composite component (collapsiblePanel). The component uses the "collapsible" bean to provide the toggle function. When I use the same component multiple times on a page, each instance of the component is bound to the same bean instance. How Can I achieve something like a component scoped bean?
collapsibleTemp.xhtml:
<cc:interface>
<cc:attribute name="model" required="true">
<cc:attribute name="collapsed" required="true" />
<cc:attribute name="toggle" required="true"
method-signature="java.lang.String f()" />
</cc:attribute>
<cc:actionSource name="toggle" />
<cc:facet name="header" />
<cc:facet name="body" />
</cc:interface>
<cc:implementation>
<h:panelGroup layout="block" styleClass="collapsiblePanel-header">
<h:commandButton id="toggle" action="#{cc.attrs.model.toggle}"
styleClass="collapsiblePanel-img"
image="#{cc.attrs.model.collapsed ? '/resources/images/plus.png' : '/resources/images/minus.png'}" />
<cc:renderFacet name="header" />
</h:panelGroup>
<h:panelGroup layout="block" rendered="#{!cc.attrs.model.collapsed}">
<cc:insertChildren />
<cc:renderFacet name="body"></cc:renderFacet>
</h:panelGroup>
<h:outputStylesheet library="css" name="components.css" />
</cc:implementation>
The backing bean:
#ManagedBean
#ViewScoped
public class Collapsible {
private boolean collapsed = false;
public boolean isCollapsed() {
return collapsed;
}
public void setCollapsed(boolean collapsed) {
this.collapsed = collapsed;
}
public String toggle() {
collapsed = !collapsed;
return null;
}
}
Using Page
<h:form id="someid">
<jl:collapsibletemp id="collapsiblePanel1" model="#{collapsible}">
<f:facet name="header">
<h3>
<h:outputText value="Collapsible information" />
</h3>
</f:facet>
<f:facet name="body">
<h:outputText value="do something....." />
</f:facet>
<p />
</jl:collapsibletemp>
<jl:collapsibletemp id="collapsiblePanel2" model="#{collapsible}">
<f:facet name="header">
<h3>
<h:outputText value="Collapsible information" />
</h3>
</f:facet>
<f:facet name="body">
<h:outputText value="do some tabbing" />
</f:facet>
<p />
</jl:collapsibletemp>
<jl:collapsibletemp id="collapsiblePanel3" model="#{collapsible}">
<f:facet name="header">
<h3>
<h:outputText value="Collapsible information" />
</h3>
</f:facet>
<f:facet name="body">
<h:outputText value="notice board" />
</f:facet>
<p />
</jl:collapsibletemp>
</h:form>
You can use the componentType attribute of the <cc:interface> to define a "backing component".
E.g.
<cc:interface componentType="collapsiblePanel">
...
</cc:interface>
<cc:implementation>
...
<h:commandButton action="#{cc.toggle}" ... />
...
<h:panelGroup rendered="#{!cc.collapsed}" ...>
...
</cc:implementation>
with just a com.example.components.CollapsiblePanel
#FacesComponent(value="collapsiblePanel") // To be specified in componentType attribute.
public class CollapsiblePanel extends UINamingContainer { // Important! Must extend UINamingContainer.
private boolean collapsed;
public void toggle() {
collapsed = !collapsed;
}
public boolean isCollapsed() {
collapsed;
}
}
However, when you want to have multiple of those components, then you should declare physically separate instances of them in the view. If this needs to happen dynamically, then you need to use <c:forEach> to generate physically separate instances of them instead of <ui:repeat> with a single component. Otherwise you have to map all collapsed states by the client ID inside a Map<String, Boolean>. See for an example and more background information also Getting same instance of `componentType` in composite component on every use

Resources