Input fields hold previous values only if validation failed - jsf

I came up with a strange problem. I tried to isolate the problem so following is my simplified code.
public class MyBean {
private List<Data> dataList;
Data selectedData;
public MyBean() {
dataList = new ArrayList<Data>();
dataList.add(new Data("John", 16));
dataList.add(new Data("William", 25));
}
public List<Data> getDataList() {
return dataList;
}
public void edit(Data data) {
selectedData = data;
}
public void newData() {
selectedData = new Data(null, null);
}
public Data getSelectedData() {
return selectedData;
}
public class Data {
String name;
Integer age;
Data(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
}
xhtml:
<rich:modalPanel id="pop">
<h:form>
Name: <h:inputText value="#{myBean.selectedData.name}" required="true" id="txtName"/><br/>
Age : <h:inputText value="#{myBean.selectedData.age}" required="true" id="txtAge"/>
<a4j:commandButton value="Save"/>
<a4j:commandButton value="Close" onclick="Richfaces.hideModalPanel('pop');return false;"/>
<br/>
<rich:message for="txtName"/><br/>
<rich:message for="txtAge"/>
</h:form>
</rich:modalPanel>
<h:form>
<rich:dataTable value="#{myBean.dataList}" var="data">
<rich:column>#{data.name}</rich:column>
<rich:column>
<a4j:commandLink value="Edit" action="#{myBean.edit(data)}" reRender="pop" oncomplete="Richfaces.showModalPanel('pop')"/>
</rich:column>
</rich:dataTable>
<a4j:commandButton value="New" action="#{myBean.newData()}" reRender="pop" oncomplete="Richfaces.showModalPanel('pop')"/>
</h:form>
This is the path to error:
Load the page
Click the "Edit" link in first row(popup displays)
In popup, clear the "Age" field and click "Save".(Required message shown)
Click cancel(without filling "Age" field)
Click second link.
Now it shows irrelevant data(previous data). - This is the problem
Even when I click "New" button it shows incorrect data.
This happens only if a validation is failed in the popup.
Is there a solution for this?

This problem is in JSF 2 also recognized and explained in detail in the following answer: How can I populate a text field using PrimeFaces AJAX after validation errors occur? If you were using JSF 2, you could have used OmniFaces' ResetInputAjaxActionListener or PrimeFaces' <p:resetInput> or resetValues="true" for this.
To the point, you need to clear the state of the EditableValueHolder component when it's about to be ajax-rendered, but which isn't included in the ajax-execute. To clear the state, in JSF 2 you would have used the convenience method resetValue() for this, but this isn't available in JSF 1.2 and you need to invoke the 4 individual methods setValue(null), setSubmittedValue(null), setLocalValueSet(false), setValid(true) to clear the state.
To figure out which components are to be ajax-rendered, but aren't been ajax-executed, in JSF 2 you would have used PartialViewContext methods for this, but this is not available in JSF 1.2 which hasn't standardized ajax yet. You'd need to fiddle with RichFaces specific ajax API in order to figure that. I can't tell that from top of head, so here's a kickoff example assuming that you already know the components which needs to be cleared. Imagine that the form in your popup has id="popForm" and the name input field has id="nameInput", here's how you could clear it inside the newData() method:
UIInput nameInput = (UIInput) context.getViewRoot().findComponent("popForm:nameInput");
nameInput.setValue(null);
nameInput.setSubmittedValue(null);
nameInput.setLocalValueSet(false);
nameInput.setValid(true);

do one thing on cancel action set all popup values null. now in your next click all values set to be default.
or on click set all previous values null. and set all respective values after that.

I had the same problem. if you are using Primefaces, the solution is as simple as putting resetValues="true" on your p:commandLink or p:commandButton that loads the selected item.

After validation failed if you want to remain same as input data which you have pass as submission parameter, then set value attribute as your form bean name as mention below i.e.
<input type="text" id="fname" path="fname" value="${myFormBean.fname}"/>

Related

How to Compare the values JSF selectonemenu

I am developing a JSF application
I have 2 selectOnemenu controls and submit button.
I need to disable the button if the values of 2 fields are equal
<h:selectOneMenu id="from" value="#{currencyBean.history.fromCurrency}" >
<f:selectItems value="#{currencyBean.currency}" var="c" itemValue="#{c}" itemLabel="#{c.name}"/>
</h:selectOneMenu>
<p:outputLabel for="to" value="to:" />
<h:selectOneMenu id="to" value="#{currencyBean.history.toCurrency}" >
<f:selectItems value="#{currencyBean.currency}" var="c" itemValue="#{c}" itemLabel="#{c.name}"/>
</h:selectOneMenu>
<p:commandButton id="calculateButton" value="Convert" update=":convert :historyPanel" onstart="PF('statusDialog').show()" onsuccess="PF('statusDialog').hide()" validateClient="true" ajax="true" action="#{currencyBean.Calculate()}" />
I tried to use onchange with ajax but everytime I change one dropdown the value of the second drowpdown became null in the backbean so I cannot read it.
Here is my backbean
#Named(value = "currencyBean")
#RequestScoped
public class CurrencyBean implements Serializable {
/**
* Creates a new instance of CurrencyBean
*/
private History history;
private Currency currency;
private Date currentDate;
#Inject
private Loginbean loginBean;
private List<History> historyList;
public List<History> getHistoryList() {
int userId = loginBean.getUserId();
if (userId != -1) {
return new HistoryHelper().GetHistoryListByUser(userId);
}
return null;
}
public CurrencyBean() {
history = new History();
}
public History getHistory() {
return history;
}
public void setHistory(History history) {
this.history = history;
}
public Currency[] getCurrency() {
return Currency.values();
}
public Date getCurrentDate() {
return new Date();
}
public void Calculate() {
int userId = loginBean.getUserId();
if (userId != -1) {
new CurrencyClient().Convert(userId, this.history);
}
}
}
any clue ?
My assumption is that all of your problems come from your managed bean scope. You have #Request scope so every request your managed bean will be removed from container, thus when you define onchange="submit()" (this is only my assumption because you haven't define how you implement onchange attribute) and you select value from one selectBox component values for this component is updated but the first one is still null. When you select second selectBox value updated from first selectBox doesn't exists anymore as managed bean has been removed after first request. You should try with wider scope for instance #ViewScope. If it doesn't help then further informations like implementation onchange attribute will be needed

CommandLink not Working on a Lazy Loaded Primefaces Datascroller

I'm having an issue with lazy loading a Primefaces Datascroller component.
I have a jsf page that should display 10 events on page load. If the user wants to see more he/she can click the more button to load and display the 10 next events. For each of the event rows, there is a link that can be used to display the event's details.
<h:form id="mainForm" >
<p:dataScroller value="#{backing.lazyModel}" var="event" lazy="true" chunkSize="10" rowIndexVar="index">
#{event.name}
<p:commandLink class="view-trigger"
value="View Event Details"
actionListener="#{backing.initViewEventDetails(index, event)}"/>
<f:facet name="loader">
<p:outputPanel
visible="#{backing.lazyModel.rowCount gt 10}"
rendered="#{backing.lazyModel.rowCount gt 10}">
<p:commandLink value="More" />
</p:outputPanel>
</f:facet>
</p:dataScroller>
</h:form>
The initial search works fine, that is, when I click the view event details link, my backing bean is invoked and I see that the index and event received correspond to the row I clicked on.
However, once I load the next chunk, which consists of 1 extra event, the page displays 11 events but clicking a view event details link sends the proper index but does not send the proper event. For example, if I click on event at index 0, I get the event at index 10, if I click on event at index 1 my backing bean is not invoked.
It looks like the datascroller forgets about the last 10 events when I click on the more button but my lazy data model still remembers.
The backing bean:
#ManagedBean(name="backing")
#ViewScoped
public class DataScrollerBacking implements Serializable {
private static final long serialVersionUID = 4012320411042043677L;
private static final Logger LOGGER = Logger.getLogger(DataScrollerBacking.class);
#ManagedProperty("#{settings.dataSource}")
private String dataSource;
private WebEventDAO webEventDAO;
private LazyDataModel<Event> lazyModel;
#PostConstruct
public void init() {
webEventDAO = CommonDAOFactory.getInstance(dataSource).getWebEventDAO();
search();
}
public void search() {
DateTime start = new DateTime(2014, 1, 1, 0, 0 ,0);
final Date startDate = start.toDate();
final Date endDate = start.plus(Years.ONE.toPeriod()).minus(Seconds.ONE.toPeriod()).toDate();
lazyModel = new LazyDataModel<Event>() {
private static final long serialVersionUID = 1231902031619933635L;
private LinkedHashSet<Event> eventCache; // Ordered set of all retrieved events so far.
// I'm using a set because the load method is called twice on page load (any idea why???) and I don't want duplicates in my cache.
#Override
public List<Event> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
List<Event> events = new ArrayList<Event>(10);
try {
if(eventCache == null){
int count = webEventDAO.getSearchByPeriodRaceTypeAndRaceStatusForCompanyCount(Collections.singletonList(1), startDate, endDate, null, null);
this.setRowCount(count);
eventCache = new LinkedHashSet<Event>(count);
}
events = webEventDAO.searchByPeriodRaceTypeAndRaceStatusForCompany(Collections.singletonList(1), startDate, endDate, null, null, true, first, pageSize);
eventCache.addAll(events);
} catch (DAOException e) {
LOGGER.error("An error occurred while retrieving events.", e);
}
return events;
}
};
}
public void initViewEventDetails(Integer index, Event event){
LOGGER.info("index=" + index + " eventname=" + event.getName());
}
public String getDataSource() {
return dataSource;
}
public void setDataSource(String dataSource) {
this.dataSource = dataSource;
}
public LazyDataModel<Event> getLazyModel() {
return lazyModel;
}
public void setLazyModel(LazyDataModel<Event> lazyModel) {
this.lazyModel = lazyModel;
}}
Since the page displays the proper information and the index received is always valid, my current workaround is to go fetch the Event in the lazy data model by index.
However, I would like to understand why the received event is not the one I clicked on.
Am I doing something wrong or this is just how the scroller is implemented?
Running on Mojarra 2.2, Tomcat 7, Primefaces 5, Omnifaces 1.8
I found a good explanation about the behavior of request scope in this link http://www.theserverside.com/news/thread.tss?thread_id=44186
If you are using ManagedBeans in request scope, you get problems with
CommandLinks inside DataTables. DataTables are one thing I really like
about JSF, and CommandLinks often come in handy as well. But when you
put a CommandLink inside a DataTable, e. g., to select the entry of
the row in which the CommandLink is, you get bitten. That is, if you
want ManagedBeans with request scope. The action which should be
triggered by the CommandLink is never triggered, the page is simply
rendered again. The reason for this behaviour is that the DataTable
modifies the id of the CommandLink during renderering, but the
CommandLink does not know that it was rendered with a different id.
During the decoding of the request which was triggered by clicking the
CommandLink, the ComandLinkRenderer looks at a hidden form parameter.
If the value of that form parameter equals the id of the CommandLink,
an action is queued. If not, nothing is done. Since the DataTable
changes the ids, the value of the hidden form parameter does not match
the id of the CommandLink.
Based on above context, you need to change the scope annotations from #ViewScoped to
#SessionScope, and your problem will be solved automatically. It seems to be a better solution than write additional code, unless you need to keep the #ViewScopped
A workaround would be to use PrimeFaces remote command, passing arguments with rc([name: 'paramName', value: someParamValue]). These arguments should be available using #{param['paramName']} EL expression
Example:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:body>
<ui:composition>
<p:dataTable id="#{id}" widgetVar="#{id}"
value="#{requestCache.getLazy(id, () -> dataSource)}" var="rpo"
selectionMode="single" selection="#{selection}"
lazy="true" paginator="true" rows="#{pageSizeController.pageSize}"
pageLinks="10" paginatorPosition="top"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
currentPageReportTemplate="(#{label.Page} {currentPage} #{label.of} {totalPages}, #{label.Row} {startRecord} - {endRecord} #{label.of} {totalRecords})"
rowsPerPageTemplate="5 10 20 30 40 50 100"
scrollable="true" scrollHeight="#{empty scrollHeight ? 300 : scrollHeight}"
resizableColumns="true" emptyMessage="#{label.Table_is_empty}">
<p:column>
<h:outputText value="#{rpo.decreeSequence.code}" />
<p:commandLink id="displayAdditionalInfoCommandLink" type="link" style="float: right; text-decoration: none"
onclick="displayAdditionalInfo([{name: 'refundPaymentOrderId', value: #{rpo.id}}])"
title="#{label.Additional_information}">
<h:outputLabel for="displayAdditionalInfoCommandLink" styleClass="fa fa-info-circle"
onmouseover="jQuery(this).addClass('fa-lg').css('cursor', 'pointer')"
onmouseout="jQuery(this).removeClass('fa-lg')"/>
</p:commandLink>
</p:column>
</p:dataTable>
<p:remoteCommand name="displayAdditionalInfo" process="#this" update="#parent">
<f:setPropertyActionListener target="#{refundPaymentOrderCache.refundPaymentOrder}"
value="#{refundPaymentOrderRepo.find(requestCache.toLong(param['refundPaymentOrderId']))}" />
<f:actionListener binding="#{dialog.displayInputForm('RPO_ADDITIONAL_INFO')}" />
</p:remoteCommand>
</ui:composition>
</h:body>
</html>
I finally had time to spend on this issue and I found a workaround. It's a hack so maybe the proper solution would be to use a different component or create my own.
It seems like Primefaces DataScroller limitation that occurs when using the DataScroller with a LazyDataModel. It would seem that the component was not designed to do this.
To avoid this issue, I implemented my own lazy loading where the same list instance is returned in addition to the newly added elements.
Here is my previous example modified to implement this new lazy loading pattern:
The html page:
<h:form id="mainForm" >
<p:dataScroller value="#{backing.events}" var="event" rowIndexVar="index">
#{event.name}
<p:commandLink class="view-trigger"
value="View Event Details"
action="#{backing.initViewEventDetails(index, event)}"/>
<f:facet name="loader"><h:outputText value=""/></f:facet>
</p:dataScroller>
<p:commandLink value="More" process="#form" update="#form"
action="#{backing.loadMore()}"
visible="#{backing.totalCount gt backing.events.size()}"
rendered="#{backing.totalCount gt backing.events.size()}"/>
</h:form>
The DataScroller no longer has lazy="true", chunkSize="10", uses a list called events as the value and declares an empty loader facet (to avoid auto-load more when the bottom of the list is reached). I used a commandLink that calls backing.loadMore() and updates the form to replace the loader facet.
The backing bean:
#Named("backing")
#ViewScoped
public class DataScrollerBacking implements Serializable {
private static final long serialVersionUID = 4012320411042043677L;
private static final Logger LOGGER = Logger.getLogger(DataScrollerBacking.class);
private static final Integer CHUNK_SIZE = 10;
#DataSource
#Inject
private String dataSource;
private WebEventDAO webEventDAO;
private List<Event> events;
private Integer totalCount;
private Date startDate;
private Date endDate;
#PostConstruct
public void init() {
webEventDAO = CommonDAOFactory.getInstance(dataSource).getWebEventDAO();
search();
}
public void search() {
DateTime start = new DateTime(2014, 1, 1, 0, 0 ,0);
startDate = start.toDate();
endDate = start.plus(Years.ONE.toPeriod()).minus(Seconds.ONE.toPeriod()).toDate();
try {
totalCount = webEventDAO.getSearchByPeriodRaceTypeAndRaceStatusForCompanyCount(Collections.singletonList(1), startDate, endDate, null, null);
events = new ArrayList<Event>(totalCount);
loadMore();
} catch (DAOException e) {
LOGGER.error("An error occurred while retrieving events.", e);
}
}
public void loadMore() {
List<Event> newEvents = new ArrayList<Event>(CHUNK_SIZE);
try {
newEvents = webEventDAO.searchByPeriodRaceTypeAndRaceStatusForCompany(Collections.singletonList(1), startDate, endDate, null, null, true, events.size(), CHUNK_SIZE);
events.addAll(newEvents);
} catch (DAOException e) {
LOGGER.error("An error occurred while retrieving events.", e);
}
}
public void initViewEventDetails(Integer index, Event event){
LOGGER.info("index=" + index + " eventname=" + event.getName());
}
public String getDataSource() {
return dataSource;
}
public void setDataSource(String dataSource) {
this.dataSource = dataSource;
}
public List<Event> getEvents() {
return events;
}
public void setEvents(List<Event> events) {
this.events = events;
}
public Integer getTotalCount() {
return totalCount;
}
public void setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
}}
In the backing bean, the search method counts the total number of events, saves that information and calls loadMore() to load the first 10 events in the events list.
When the more button is clicked, loadMore() is called again and the next 10 events are appended at the end of events list.
Now when I click on newly loaded elements, the commandLink invokes the backing bean with the correct value.

Values of h:inputText inside ui:repeat are not processed

I want to process this form (valueChangueListener is not valid in real case).
This is the back bean:
public class TestBean extends PrivateBaseBean implements Serializable {
private List<String> strings;
#PostConstruct
public void init() {
strings = new ArrayList<String>();
strings.add("");
strings.add("");
strings.add("");
}
public void saveAction(ActionEvent event) {
StringBuilder textToShowInMessage = new StringBuilder();
for (String string : strings) {
textToShowInMessage.append(string);
textToShowInMessage.append("; ");
}
FacesMessage msg = new FacesMessage(super.getBundle().getString(
textToShowInMessage.toString()), "");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
getters... setters...
An the view:
....
<h:form>
<ui:repeat var="string" value="#{testBean.strings}">
<h:inputText value="#{string}" />
<br />
</ui:repeat>
<p:commandButton value="#{msg.save}"
actionListener="#{testBean.saveAction}" icon="ui-icon-disk"
update="#form" />
</h:form>
...
When the form is processed in the back bean string list always is blank.
How to process form intput's inside iteration, without any value changue listener?
There are some screenshots:
The same problem occurs with action or actionListener on
Your problem is not connected with PrimeFaces <p:commandButton>'s behaviour, but rather with a scoping problem that is implicilty created when using the <ui:repeat> tag.
First of all, let's depart from your example. Basically, you've got
<ui:repeat value="#{bean.strings}" var="s">
<h:inputText value="#{s}"/>
</ui:repeat>
with the backing List<String> strings.
The culprit is here: value="#{s}". The exported by <ui:repeat> variable s is visible only within its loop and it is not bound to any managed bean's property, but instead only to a local variable. Put it differently, s is not bound/equal to bean.strings[index] as one would expect and has no knowledge, as we see, where it originated from. So basically, you're off with a unilateral relationship: value from the bean is printed in your input properly, but the reverse is not happening.
The workarounds
Workaround #1: wrapper classes / model objects
The situation can be overcome by using a wrapper object for your class. In case of a string it could be a 'simple mutable string', like below:
public class MString {
private String string;//getter+setter+constructor
}
In this case the iteration will be working as predicted:
<ui:repeat value="#{bean.mstrings}" var="ms">
<h:inputText value="#{ms.string}"/>
</ui:repeat>
with the backing List<MString> mstrings.
Note that if you have your model class, like User, and will change its properties within <ui:repeat> the class itself will be effectively a wrapper, so that the properties will be set appropriately.
Workaround #2: chained property access
Another workaround consists of accessing an element of your collection directly from within a <h:inputText> tag. This way, any such property will be set by accessing the bean, then collection, then setting the property at the desired index. Excessively long, but that's how it is. As to the how question, <ui:repeat> provides for an exported current iteration status variable, varStatus, that will be used to access the array/collection in the managed bean.
In this case the iteration will also be working as predicted:
<ui:repeat value="#{bean.strings}" var="s" varStatus="status">
<h:inputText value="#{bean.strings[status.index]}"/>
</ui:repeat>
with the ordinary backing List<String> strings.
My workaround solution take the value directly from the page:
<ui:repeat id="repeat" value="#{bean.strings}" var="s" varStatus="status">
<h:inputText id="x" value="#{s.field}"/>
<h:commandLink style="margin: .5em 0" styleClass="commandLink" actionListener="#{bean.save(status.index)}" value="#{bundle.Send}"/>
</ui:repeat>
The save method:
public void save(String rowid) {
String jsParam = Util.getJsParam("repeat:" + rowid + ":x");
System.out.println("jsParam: " + jsParam); //persist...
}
The getJsParam method:
public static String getJsParam(String paramName) {
javax.faces.context.FacesContext jsf = javax.faces.context.FacesContext.getCurrentInstance();
Map<String, String> requestParameterMap = jsf.getExternalContext().getRequestParameterMap();
String paramValue = requestParameterMap.get(paramName);
if (paramValue != null) {
paramValue = paramValue.trim();
if (paramValue.length() == 0) {
paramValue = null;
}
}
return paramValue;
}

JSF Dynamic Rendered Component's Value becomes null when the form is submitted

I have a JSF page which renders a text field depending on the value of a drop down using primefaces ajax listner. The dynamic rendering is done fine. but the problem is once I submit the form the the bound value of that textfield doesn't get bound instead it is shown as null.
this is the part of my JSF only the necessary fields are included here
<h:panelGroup id="textPanel" >
<h:form id="main" prependId="false">
<h:outputText value="WorkFlow ID:" />
<h:selectOneMenu id="workFlows" value="#{workFlowSelectionController.selectedWorkFlowId}" >
<p:ajax event="change" listener="#{workFlowSelectionController.dropDownChange}" update="textPanel"/>
<f:selectItems value="#{workFlowSelectionController.allActiveworkFlows}"/>
</h:selectOneMenu>
<p:inputText value="#{workFlowSelectionController.texField}" rendered="#{workFlowSelectionController.textfieldVisibility}"/>
<p:commandButton ajax="false" value="Next" action="#{workFlowSelectionController.addWorkFlowselectionDetails}"/>
</h:form>
</h:panelGroup>
this is my managed bean
#ManagedBean
#RequestScoped
public class WorkFlowSelectionController {
private boolean textfieldVisibility = false;
private String texField;
public void dropDownChange() {
logger.info("WorkFlowSelectionController.dropDownChange() entered");
if (selectedWorkFlowId != null) {
if (selectedWorkFlowId.equals("-1")) {
textfieldVisibility = true;
operationListStatus = false;
} else {
textfieldVisibility = false;
operationListStatus = true;
}
} else {
textfieldVisibility = false;
operationListStatus = true;
}
public void addWorkFlowselectionDetails() throws CloneNotSupportedException {
System.out.println("Selected Value of Text Field is" + texField);
}
public String getTexField() {
return texField;
}
public void setTexField(String texField) {
this.texField = texField;
}
}
i haven't included the dropdown code of the backing bean. i just need an idea of what i am doing wrong here if i remove the rendered attribute of the textfield it works fine.
thank you
Put the bean in the view scope instead of request scope. A request scoped is recreated on every single HTTP request. The boolean property will default to false again whenever you submit the form, so the submitted value won't be processed then.
#ManagedBean
#ViewScoped
public class WorkFlowSelectionController {
//
}
A view scoped bean will live as long as you're (ajax-) interacting with the same view by returning null or void from action(listener) methods.

jsf datatable row selection problem

I have a problem with selecting rows in the Primefaces Datatable. I use dynamic columns, so the standard row selection mechanism is not usable here, I implement checkbox selection myself.
To help, here's s simplified version of what I have in my xhtml:
<h:form>
<p:dataTable id="table"
var="result"
value="#{tableBean.results}">
<p:columns value="#{tableBean.columnNames}" var="column" columnIndexVar="colIndex">
<f:facet name="header">
#{column}
</f:facet>
<h:panelGroup rendered="#{colIndex==0}">
<h:outputLabel>#{rowIndex}</h:outputLabel>
<h:selectBooleanCheckbox value="#{tableBean.selectedRows[result[0]]}"/>
</h:panelGroup>
</p:columns>
</p:dataTable>
<h:commandButton value="Submit"></h:commandButton>
</h:form>
And here's what I have in the managed bean to select the checkboxes:
package testpackage;
import java.util.*;
import javax.faces.bean.*;
#ManagedBean
#SessionScoped
public class TableBean
{
private Map<String, Boolean> selectedRows = new HashMap<String, Boolean>();
List<List<String>> results = new LinkedList<List<String>>();
public TableBean()
{
List<String> row1 = new LinkedList<String>();
List<String> row2 = new LinkedList<String>();
row1.add("row1.ref");
row1.add("row1.id");
row1.add("row1.status");
row2.add("row2.ref");
row2.add("row2.id");
row2.add("row2.status");
results.add(row1);
results.add(row2);
//selectedRows.put("row2.ref", true);
}
public Map<String, Boolean> getSelectedRows()
{
return selectedRows;
}
public String submit()
{
List<List<String>> selectedResults = new ArrayList<List<String>>();
for (List<String> result : results)
{
if (selectedRows.get(result.get(0)) != null)
{
selectedResults.add(result);
selectedRows.remove(result.get(0));
}
}
return null;
}
public List<List<String>> getResults()
{
return results;
}
public List<String> getColumnNames()
{
List<String> columnNames = new LinkedList<String>();
columnNames.add("");
columnNames.add("REF");
columnNames.add("ID");
columnNames.add("STATUS");
return columnNames;
}
}
The getSelectedRows method works great, but the problem is that the setSelectedRows method is never called, so I don't know which checkboxes the user has selected. Maybe I overlook something very trivial, but cannot find the solution.
Any ideas on this? I would be very glad if you helped, or give any other row selection solution for the dynamic columns.
Thx in advance,
Levi
To me it looks you are rendering the wrong field in selectBooleanCheckBox.
You should be using variable or field from the result variable.
My solution:
In your situation you are rendering an object from List as a form of table row so if you want to make some changes and retrieve the status of that row then you should be using the variable from that object only.
I understand you are submitting the whole form and want to pickup all updated rows, in that case you will have to loop through the whole List and find all the rows which have been updated by checking the status in Request Handler(Action) bean.
Hope that helps.
The setter is never called for nested objects. You're the one who's responsible for creating them, not JSF. JSF just gets the nested object and then calls the setter on it (which is the put() method in case of a Map). You just need to determine the selected rows in the action method. Add an action method to the commandbutton:
<h:commandButton value="Submit" action="#{bean.submit}"></h:commandButton>
which is definied like follows (guessing/assuming that var="result" is in essence an Object[]):
public String submit() {
List<Object[]> selectedResults = new ArrayList<Object[]>();
for (Object[] result : results) {
if (selectedRows.get((String) result[0])) {
selectedResults.add(result);
selectedRows.remove(result[0]); // Reset.
}
}
// Now selectedResults contains all selected results.
}

Resources