I have an application with a risk matrix. The headings are 5 severities (from the severitiesBean in my example) and 5 likelihoods (from the likelihoodsBean in my example). (The headings go by different names in literature, such as exposure or probability or consequences.) In each cell there is a risk rating (enumerated from the riskRatingsBean). To give you an idea of what this looks like visually, there is an example here.
I am able to display the matrix just fine, which seems all people asking related questions on SO cared about. However, I want to display and allow the administrator of the system to edit the risk for each cell in the grid. A selectOneMenu dropdown in either h: or p: (PrimeFaces) seems like a good way of allowing the selection.
Here's the JSF:
<h:panelGroup id="editArea">
<table border="1" style="border-collapse:collapse;" class="riskMatrix">
<tr>
<td rowspan="2" colspan="2"></td>
<th colspan="5">Severity</th>
</tr>
<tr>
<!-- Severities going across the top -->
<ui:repeat var="severity" value="#{severitiesBean.severities}">
<th style="text-align: center;">
<h:outputText value="#{severity.name}"/>
</th>
</ui:repeat>
</tr>
<ui:repeat id="likelihoodLoop" var="likelihood" value="#{likelihoodsBean.likelihoods}" varStatus="lkhdvar">
<tr>
<ui:fragment rendered="#{lkhdvar.index == 0}">
<th rowspan="5">Likelihood</th>
</ui:fragment>
<!-- Likelihoods going down the side -->
<th>
<h:outputText value="#{likelihood.name}"/>
</th>
<!-- The rating for each cell going across -->
<ui:repeat id="severitiesLoop" var="severity" value="#{severitiesBean.severities}">
<td class="#{riskRatingsBean.riskRatings.get(riskMatrixBean.riskMatrix[severity.id-1][likelihood.id-1]-1).cssClass}" style="text-align: center;">
<p:selectOneMenu
id="RiskRatingDropDown" effect="none"
value="#{riskMatrixBean.riskMatrix[severity.id-1][likelihood.id-1]}">
<f:selectItems value="#{riskRatingsBean.riskRatings}" var="rating" itemLabel="#{rating.name}" itemValue="#{rating.id}"/>
</p:selectOneMenu>
</td>
</ui:repeat>
</tr>
</ui:repeat>
</table>
</h:panelGroup>
<h:messages id="riskMatrixErrorMessages"/>
<p:commandButton id="SaveButton" value="Update" actionListener="#{riskMatrixBean.saveRiskMatrix}"
update="editArea"/>
In the riskMatrixBean, the matrix is defined like this:
public int[][] getRiskMatrix()
{
return riskMatrix;
}
This all displays perfectly. However, I want to be able to update the values. When I click on the submit button, I get an exception:
javax.faces.component.UpdateModelException: java.lang.ClassCastException: Unable to add an object of type [java.lang.Integer] to an array of objects of type [int]
at javax.faces.component.UIInput.updateModel(UIInput.java:866)
at javax.faces.component.UIInput.processUpdates(UIInput.java:749)
at com.sun.faces.facelets.component.UIRepeat.process(UIRepeat.java:633)
at com.sun.faces.facelets.component.UIRepeat.processUpdates(UIRepeat.java:880)
at com.sun.faces.facelets.component.UIRepeat.process(UIRepeat.java:633)
at com.sun.faces.facelets.component.UIRepeat.processUpdates(UIRepeat.java:880)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1291)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1291)
at javax.faces.component.UIForm.processUpdates(UIForm.java:281)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1291)
at javax.faces.component.UIViewRoot.processUpdates(UIViewRoot.java:1254)
at com.sun.faces.lifecycle.UpdateModelValuesPhase.execute(UpdateModelValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:650)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:357)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:581)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:931)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassCastException: Unable to add an object of type [java.lang.Integer] to an array of objects of type [int]
at javax.el.ArrayELResolver.setValue(ArrayELResolver.java:96)
at com.sun.faces.el.DemuxCompositeELResolver._setValue(DemuxCompositeELResolver.java:255)
at com.sun.faces.el.DemuxCompositeELResolver.setValue(DemuxCompositeELResolver.java:281)
at org.apache.el.parser.AstValue.setValue(AstValue.java:218)
at org.apache.el.ValueExpressionImpl.setValue(ValueExpressionImpl.java:253)
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:131)
at javax.faces.component.UIInput.updateModel(UIInput.java:832)
... 33 more
I'm at a loss as to what I'm doing wrong. Obviously the updateModel during the updateModelValuesPhase doesn't know how to deal with the int[][] and how I've specified the selectOneMenu value attribute. I suspect there is something wrong with the approach, but I'm not sure what. Maybe it's something to do with the nested ui:repeats? I tried to change to using Integer[][] and to List<List<Integer>>, and still no luck, although I probably did something else wrong along the way. I'm open to changing my data type as necessary.
Thanks in advance!
Related
I have a dataGrid with 2 columns and a panelGrid inside it that shows the data correctly, but it fills both columns before changing row. I need it to fill first column, with half of list content and only then go to second column and fill it with rest. It is a static list with an even number right now, but it would be best if it could be done with a dynamic sized list with possible odd total number of values. There is an answer here in SO that shows that asp has a repeatcolumn tag that (if i understood correctly) does what i need. Is there a way to do that using richfaces in jsf?
<fieldset>
<legend>Select topics:</legend>
<rich:dataGrid value="#{registerForm.topics}"
var="topic"
columns="2">
<h:panelGrid columns="2" width="430px"
columnClasses="checkTopic,labelTopic" border="0">
<h:selectBooleanCheckbox id="checkTopic"
align="left"
value="#{registerForm.SelectedTopic}"
disabled="#{not registerForm.ActiveRegister}"/>
<h:outputLabel value="#{topic.description}"
for="checkTopic" />
</h:panelGrid>
</rich:dataGrid>
<h:panelGroup rendered="#{empty registerForm.topics}"
style="color: red;">
No topics registered.
</h:panelGroup>
</fieldset
I think I should use ui:repeat, but cant figure out how. I tried using dataList and dividing the list in 2 parts and each shown in a separed row, but didnt look good, also the code feels overcomplicated than it should be.
Also this question is the same as mine, but the answer doesnt correspond exctly to what is needed and i cant comment because 50 reputation.
I need:
Value1 Value3
Value2 Value4
With the code I have(and the answer) the result is:
Value1 Value2
Value3 Value4
Well you can't change the direction of the items, but you can reorder the list so instead of [1,2,3,4,5] you'll have [1,4,2,5,3].
Alternatively you could use the rendering index (rowKeyVar) and have your bean do the math figuring out what item should be displayed (essentially ignoring the list you're passing to the component).
Solved the issue using two DataTables and the "first" attribute. Had to create "getLines" class in backend, that returns listsize/2 (roundup), because I couddnt figure out how to do it in front end (but plan on doing).
<fieldset>
<legend>Select topic:</legend>
<h:panelGrid id="topicspanel" columnClasses="topicscol,topicscol" columns="2" border="0">
<rich:dataTable value="#{registerForm.topics}"
var="topic" columns="1" rows="#{registerForm.lines}"
border="0" id="table1">
<rich:column>
<h:panelGrid columns="2" width="430px"
columnClasses="checkTopic,labelTopic" border="0">
<h:selectBooleanCheckbox id="checkTopicCol1" align="left"
value="#{registerForm.selectedTopic}"
disabled="#{not registerForm.activeRegisters}" />
<h:outputLabel value="#{topic.name}"
for="checkTopicCol1" />
</h:panelGrid>
</rich:column>
</rich:dataTable>
<rich:dataTable value="#{registerForm.topics}"
var="topic" columns="1" rows="#{registerForm.lines}"
border="0" id="table2" first="#{registerForm.lines}"
align="right">
<rich:column>
<h:panelGrid columns="2" width="430px"
columnClasses="checkTopic,labelTopic" border="0">
<h:selectBooleanCheckbox id="checkTopicCol2" align="left"
value="#{registerForm.selectedTopic}"
disabled="#{not registerForm.activeRegisters}" />
<h:outputLabel value="#{topic.name}" for="checkTopicCol2" />
</h:panelGrid>
</rich:column>
</rich:dataTable>
</h:panelGrid>
I have to put in summary many columns summarizing the results,
and in jsf datatable i cant do it, someone know how can i do it? without use primefaces
im got this result
<h:dataTable id="tableResult" value="#{managedBean.dataModel}"
var="obj" rows="15" layout="block" styleClass="table">
<h:column>
<f:facet name="header">Total abandonada</f:facet>
<h:outputText value="#{obj.totalAbandon}">
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">Total</f:facet>
<h:outputText value="#{obj.total}" />
</h:column>
<h:column>
<f:facet name="header"> Atendidas %</f:facet>
<h:outputText value="#{obj.percentualAnswered}%" />
</h:column>
<h:column>
<f:facet name="header">Abandonos %</f:facet>
<h:outputText value="#{obj.percentualAbandon}%" />
</h:column>
<f:facet name="footer">#{obj.suumary}
</f:facet>
</h:dataTable>
to got this result im using
<f:facet name="footer">Total : 2</f:facet>
but i want this result
<tr>
<td>Total : 2</td>
<td>Total answered : 1</td>
<td>Total abandon : 1</td>
</tr>
</tfoot>
i have been tried use 2 footer facet and doesnt work
A plain JSF h:datatable does not have the notion of something like a summary per group of rows built in. So if this is what you want, you need to either switch to e.g. PrimeFaces or add the summary row to your model in the right place and be creative with the CSS for the rows and the content.
If you want a footer for the full datable, the footer facet is the right thing (a summary of the datatable). But in this footer you cannot use the variable you assigned in the var attribute, in your case obj Think of it, which obj should be used? The first? Second? Last? All (each)? So add a field to the managedBean that contains the 'summary' and display that.
<f:facet name="footer">#{manageBean.summary}</f:facet>
If summary is a list, you can iterate over it like mentioned in Iterate over nested List property inside h:datatable
The html you'll end up is
<tfoot>
<tr>
<td>
.... your content of the nested list
</td>
</tr>
</tfoot>
This contnt can be either a table, list, div's or whatever you want.
Does anybody know what h:dataTable bodyrows means? I tried a simple example, but I don't understand what it's supposed to do.
<h:dataTable bodyrows="d" value="#{index.publishDates}" var="d">
Is this some sort of shortcut for making a table? I don't see any rows because of the bodyrows annotation. If h:column does columns, what does bodyrows do?
I don't understand the documentation.
This must be a comma separated list of integers. Each entry in this list is the row index of the row before which a "tbody" element should be rendered.
In HTML, a <table> can have multiple bodies via <tbody>.
<table>
<tbody>...</tbody>
<tbody>...</tbody>
<tbody>...</tbody>
</table>
By default, a <h:datatable> generates only one body like below.
<h:dataTable value="#{[1,2,3,4,5]}" var="i">
<h:column>#{i}</h:column>
</h:dataTable>
<table>
<tbody>
<tr><td>1</td></tr>
<tr><td>2</td></tr>
<tr><td>3</td></tr>
<tr><td>4</td></tr>
<tr><td>5</td></tr>
</tbody>
</table>
The bodyrows attribute can be used to specify a commaseparated string of row indexes which should start as a new body.
<h:dataTable value="#{[1,2,3,4,5]}" var="i" bodyrows="0,2,4">
<h:column>#{i}</h:column>
</h:dataTable>
<table>
<tbody>
<tr><td>1</td></tr>
<tr><td>2</td></tr>
</tbody>
<tbody>
<tr><td>3</td></tr>
<tr><td>4</td></tr>
</tbody>
<tbody>
<tr><td>5</td></tr>
</tbody>
</table>
See also:
Can we have multiple <tbody> in same <table>?
HTML Dog HTML beginner tutorial - tables
MDN HTML element reference - table
I have a richtree like this :
<rich:tree id="positionTree"
value="#{positionAdminBean.positionTree}"
var="pos" switchType="ajax" binding="#positionAdminBean.htmlTree}"
nodeSelectListener="#{positionAdminBean.onCmdSelectPosition}"
ajaxSubmitSelection="true" reRender="positionPanel,mainTabbedPane,selectGroupPanel">
<rich:treeNode id="treeNodeId">
<table width="100%">
<tr>
<td width="50%">
<h:outputText value="#{pos.name}" id="treeposNameId"/>
</td>
<td width="40%">
<h:outputText value="#{pos.humanResourceName}" id="treeposHumanResName"/>
</td>
</tr>
</table>
</rich:treeNode>
</rich:tree>
these codes generate and show a tree of data correctly.
but my problem is that when you click on a row it fires nodeSelectListener="#{positionAdminBean.onCmdSelectPosition}".
as you see my tree has two columns , I need to do something with this code that every columns has its own select listener .
or do something that when user click on each columns it does different works
Well, a nodeSelectListener is fired when a node is selected. You can not assign it to something that is only a part of the node.
You can define an <a4j:jsFunction> and call it when the table cell is clicked.
<a4j:jsFunction name="firstColumnListener" actionListener="#{bean.doSomething}" … >
<a4j:param name="id" assignTo="#{bean.selectedId}" />
</a4j:jsFunction>
<td onclick="firstColumnListener(id)">
…
</td>
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.