jsf repeat inside datatable - jsf

I'm using datatable and i get number of columns dynamically. I tried to put repeat inside like this way:
<h:dataTable value="#{movieUserBean.sits}" var="row"
rowClasses="oddRows,evenRows" headerClass="header"
styleClass="table" cellpadding="5" cellspacing="0">
<ui:repeat value="#{row}" var="sit">
<h:column>
<h:selectBooleanCheckbox value= "#{movieUserBean.checked[sit.id]}"/>
</h:column>
</ui:repeat>
Which sits is a two dimensions array, my thought was that datatable should loop over the sits rows and repeat loops over every value inside every row (which is an object named Sit with get method: getId).
The problem is that I get an empty table. Seems like var "sit" is not getting value.
Can the problem be the fact that datatable ignores any elements who's not in column scope?

ui:repeat is called before the render response phase, i suggest you to use DynamicColumns of primefaces http://www.primefaces.org/showcase/ui/datatableDynamicColumns.jsf

A simple solution would be
<table>
<ui:repeat value="#{movieUserBean.sits}" var="row">
<tr>
<ui:repeat value="#{row}" var="sit">
<td><h:selectBooleanCheckbox value= "#{movieUserBean.checked[sit.id]}"/></td>
</ui:repeat>
</tr>
</ui:repeat>
</table>
If you have variable columns per row it might mess up with css styles. For example 5 columns on row one and 3 columns on row 2, how would you style them. The way i see it you want to show seats in a theater occupied with a checkbox.
You can try using ui:repeat's with ul,li or div's instead of going with the table. approach.

Related

How do I get h:outputText inside of a ui:repeat nested in h:dataTable to update when the datatable is updated?

So the relevant sections of my code is:
<h:panelGroup id="pnlGrp" style="padding:10px" width="100%">
<div>
<h:dataTable id="availableCrList"
value="#{searchData.availableCrList}"
var="avail"
varStatus="thisVarStatus" rows="#{searchData.rowsPerPage}"
sortColumn="#{searchData.crSortColumnName}"
sortAscending="#{searchData.crAscending}" style="width:100%;">
<h:column>
<ui:repeat value="#{avail.crRsnCdList}"
var="crRsnCd"
varStatus="status">
<h:outputText value="#{crRsnCd}<br />"
title="#{avail.crRsnDescList[status.index]}"
escape="false"/>
</ui:repeat>
</h:column>
</h:dataTable>
</div>
</h:panelGroup>
I'm working inside a legacy application and my goal is to display a list of codes which indicate reasons why a given item in this table might be completed, or cancelled, et cetera. The title is converted into a hovering tooltip which displays the description for each of these codes.
Now, I managed to get this working for singular codes no problem, but since switching from a String to a List of strings it's been a nightmare trying to get this to work.
Right now with the above code it displays the codes correctly the first time, but when I update the datatable by searching for a new value all the other columns (not shown) are correctly displayed while the RsnCd column continues to display the same data from the first search.
As an example, the first time I search for records the datatable might pull up:
Row1:A1
Row2:A1
Row3:A1
A3
Row4:A1
The second time I search for data I would expect to see only:
Row1:
Row2:
Row3:
Row4:
Row5:
Row6:
Row7:
But instead I get:
Row1:A1
Row2:A1
Row3:A1
A3
Row4:A1
Row5:
Row6:
Row7:
Really not sure if I'm explaining this adequately/understandably.
I solved this issue by changing
<ui:repeat value="#{avail.crRsnCdList}"
var="crRsnCd"
varStatus="status">
<h:outputText value="#{crRsnCd}<br />"
title="#{avail.crRsnDescList[status.index]}"
escape="false"/>
</ui:repeat>
To:
<ice:column>
<ice:repeat value="#{avail.crRsnCdList}"
varStatus="status">
<ice:outputText value="#{avail.crRsnCdList[status.index]}<br />"
title="#{avail.crRsnDescList[status.index]}"
escape="false"/>
</ice:repeat>
</ice:column>
I tried getting rid of the value but it stopped displaying anything.

Render child component when parent component rendered property is false?

I want to create a grid like this:
Element1 Element2 Element3
Element4 Element5 Element6
I have the following code:
<ui:repeat value=#{beans.myElementList} var="element" varStatus="i">
<b:row rendered=#{i.index%3==0}>
<b:column medium-screen="4">
#{element.display}
</b:column>
</b:row>
</ui:repeat>
The result of my code:
Element1
Element4
How to solve this problem?
<b:panelGrid> to the rescue:
<ui:repeat value=#{beans.myElementList} var="element">
<b:panelGrid columns="3" size="md">
#{element.display}
</b:panelGrid>
</ui:repeat>
<b:panelGrid> is inspired by the standard <h:panelGrid>, which renders an HTML table. Similarly, <b:panelGrid> renders a table consisting of Bootstrap rows and columns. Simply put everything you want to display into the panel grid. BootsFaces automatically detects when to render a new row.
The use case I originally had in mind is a form. More often than not, such a form is a repetition of identical lines: label, input field, error message. <b:panelGrid> allows you to create tabular forms like this with minimal effort.
Also see the documentation of <b:panelGrid>.
Addition until BootsFaces 1.2.0 is released:
Looking at the documentation, I wasn't happy what I saw. So I've corrected and updated it. Until BootsFaces 1.2.0 is released, also see the documentation of the developer showcase.
Try the below code.
The first ui:repeat renders <row> for each 3 elements, the second one renders elements (within <column>) in groups of 3 elements each.
<ui:repeat value="#{beans.myElementList}" step="3" varStatus="i" >
<b:row>
<ui:repeat value="#{beans.myElementList}" var="element"
step="1" offset="#{i.index}"
size="#{i.index + 3 le beans.myElementList.size() ? i.index + 3 : beans.myElementList.size() }"
varStatus="j" >
<b:column medium-screen="4">
#{element.display}
</b:column>
</ui:repeat>
</b:row>
</ui:repeat>

Empty h:dataTable in JSF renders empty row with invalid number of columns?

I have the following JSF code which renders a table from a Bean which might be empty
<h:dataTable value="#{recController.currentRecList}" var="rec" styleClass="display" id="rec">
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['rec.name']}"/>
</f:facet>
#{rec.name}
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['rec.notes']}"/>
</f:facet>
#{rec.notes}
</h:column>
</h:dataTable>
problem is the above code renders the following HTML:
<tbody><tr><td></td></tr></tbody>
The above is wrong because it should render:
<tbody><tr><td></td><td></td></tr></tbody>
where the number of columns is two and not one! but even better it should render just:
<tbody></tbody>
So that DataTables js script recognizes there's no rows and shows a nice "Empty table".
Currently DataTables js script returns an error:
DataTables warning: table id=rec - Requested unknown parameter '1' for row 0.
For more information about this error, please see http://datatables.net/tn/4
of course because it sees a row but with an incorrect number of columns.
Now using Primefaces p:dataTable produces even more boilerplate html code because the table is buried inside several div's and when an empty result is returned it produces a:
<tr class="ui-widget-content ui-datatable-empty-message">
<td class=" " colspan="4">No records found.</td>
</tr>
So again DataTables js (http://datatables.net/) finds a wrong number of columns.
Is there a way to tell primefaces what html code to output when no result is found?
Ok I finally found a workaround by using jQuery to check the html and remove the offending column, this is rather hacky but seems noone has any workaround and both standard JSF datatable and Primefaces datatable have the same issue.
<script type="text/javascript" charset="utf-8">
var oTable;
jQuery(document).ready(function() {
/* JSF has a bug where in the case of an empty list it generates a table with
* a single row and a single column no matter how many columns where defined.
* This breaks DataTables script so we manually delete this offending row if
* it is present */
length = jQuery("#myTable tbody tr").first().children().length;
alert(length);
if(length===1)
{
jQuery("#myTable tbody").html("");
}
oTable = jQuery('#myTable').dataTable({
"sDom": 'TRlfrCtip',
"oTableTools": {
"sSwfPath": "#{resource['tabletools/swf/copy_csv_xls_pdf.swf']}"
}
});
//fnClickAddRow();
});
</script>

Create a dynamic table from a java.util.Map

I'm developing a web application in JSF 2.0. In my application I need a table for viewing presences.
I have a Map<Date, List<Presence>> in my 'PresenceService' bean.
The Presence object has a Person object (with name and surname) and a boolean present/not present.
I'd like to create a table that looks like this:
monday tuesday wednesday ...
Name Person 1 present not present present ...
Name Person 2 present present present ...
... ... ... ... ...
Which JSF components could I use to create such a table? Or should I not be using a Map for this? I tried creating a table myself with <ui:repeat>'s but failed to do so.
As wel the colums as the rows can be dynamic since the user can choose a period and a range of persons.
EDIT: solution (see answer Balus C)
This works like a charm:
<table>
<tr>
<th>Name</th>
<ui:repeat value="#{aanwezigheidController.siAanwezigheidService.datums}" var="datumHeader">
<th>
<h:outputText value="#{datumHeader}">
<f:convertDateTime pattern="dd/MM/yyyy" />
</h:outputText>
</th>
</ui:repeat>
</tr>
<ui:repeat value="#{aanwezigheidController.siAanwezigheidService.aanwezigheidSiRijen}" var="aanwRij">
<tr>
<td>#{aanwRij.persoon.naam} #{aanwRij.persoon.voornaam}</td>
<ui:repeat value="#{aanwezigheidController.siAanwezigheidService.datums}" var="datumCell">
<td>
<h:outputText value="aanwezig" rendered="#{aanwRij.aanwezighedenMap[datumCell].aanwezig}" />
<h:outputText value="afwezig" rendered="#{!aanwRij.aanwezighedenMap[datumCell].aanwezig}" />
</td>
</ui:repeat>
</tr>
</ui:repeat>
</table>
A double nested <ui:repeat> should be fine. The standard JSF implementation does not offer something like <h:columns> to represent dynamic columns, but component libraries RichFaces and PrimeFaces have respectively <rich:columns> and <p:columns> for exactly this purpose. You may want to take a look at it.
With alone a Map<Date, List<Presence>>, it is not possible to pick a specific person to present the row data for all columns. You basically need a Map<Date, Map<Person, List<Presence>>, or a separate List<Date> representing the dynamic columns and a separate List<Presence> where the Presence in turn has Person and Map<Date, Boolean> properties so that a specific date can be picked for the columns.

JSF 2 Composite Component rendering problem

My composite component (cc) creates an inputText-Field. The important part is, that it is rendered depending on the models property "visible". The model is given to the component via parm "name".
<cc:interface>
<cc:attribute name="name" required="true"/>
</cc:interface>
<cc:implementation componentType="ch.sbi.pt.components.PMSInputText">
<h:inputText value="#{cc.attrs.name.value}" rendered="#{cc.attrs.name.visible}"/>
</cc:implementation>
In the view i have a panelGrid with 2 cells/row: the first row has a label and my cc, the second is not important. The label renders itself with the same model-property as my cc does.
<h:panelGrid columns="2">
<h:outputText value="Name" rendered="#{person.name.visible}"/>
<sbic:pmsInputText name="#{person.name}"/>
<h:outputText value="Next Label"/>
<sbic:pmsInputText name="#{something.name}"/>
</h:panelGrid>
The result (and problem) is the following, if "visible"-property returns "false":
None of the components are rendered (perfect!) BUT the cc resulting HTML leaves an empty cell (e.g. <td></td>) which results in a ugly layouted HTML-Table (offset one cell):
<table>
<tbody>
<tr>
<td></td>
<td>Next Label</td>
</tr>
....
As far as I understand this has to do with the lifecycle (jstl vs. jsf): my cc renders before the <h:outputText../> but how can i get rid of the empty cell (e.g. <td></td>)? Am i missing something here?
Thanx for your help, experts!
Marc
This is fully expected. The composite component is rendered. Only its children are not. You need to move the rendered attribute to the composite component instead.
<sbic:pmsInputText name="#{person.name}" rendered="#{person.name.visible}" />

Resources