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.
}
Related
I am having a two level map Map<String,HashMap<String,String>> which i need to display using a <p:dataTable>. The code of managed bean is as follows:
#ManagedBean(name="MyBean")
public class MyBean{
private Map<String,HashMap<String,String>> twoDimentionalMap;
public void getMapData(){
twoDimentionalMap=getDataFromDataStore();
}
}
Now I am using this map in my view.xhtml file as follows:
<p:dataTable var="entrySet1" value="#{MyBean.twoDimentionalMap.entrySet()}">
<p:columns var="entrySet2" value="#{entrySet1.getValue()}">
#{entrySet2.getKey()} - #{entrySet2.getValue()}
</p:columns>
</p:dataTable>
I also tried using
<p:dataTable var="entrySet1" value="#{MyBean.twoDimentionalMap.entrySet()}">
<p:columns var="entrySet2" value="#{MyBean.twoDimentionalMap.get(entrySet1.getKey()).getValue()}">
#{entrySet2.getKey()} - #{entrySet2.getValue()}
</p:columns>
</p:dataTable>
I even tried converting the outer map to a list:
List<HashMap<String,String>> twoDimentionalMap;
However nothing is displayed on datatable. The execution shows no error but there is nothing displayed on the page.
Kindly suggest if I am doing something wrong or if <p:columns> is having any issue handling maps.
Thanks
since the keys of outer map do not have a meaning, converting the outer map to a list is correct.
but your approach to retrieve column names from xhtml does not seem valid. you need to get them independently from the current iteration variable entrySet1, otherwise you add a third dimension to the operation, which data table cannot handle.
we need to assume that all keys are same across the listed maps.
here is the code for xhtml:
<p:dataTable var="entrySet1" value="#{testMB.twoDimensionalMap}">
<p:columns var="keySet2" value="#{testMB.columnNames}">
#{keySet2} - #{entrySet1[keySet2]}
</p:columns>
</p:dataTable>
and for the bean:
#Named
#ViewScoped
public class TestMB implements Serializable {
private List<HashMap<String,String>> twoDimensionalMap;
public TestMB()
{
getMapData();
}
private void getMapData(){
//twoDimentionalMap=getDataFromDataStore();
twoDimensionalMap = new ArrayList<HashMap<String,String>>();
twoDimensionalMap.add(new HashMap<String,String>());
twoDimensionalMap.get(0).put("key0", "value00");
twoDimensionalMap.get(0).put("key1", "value01");
twoDimensionalMap.add(new HashMap<String,String>());
twoDimensionalMap.get(1).put("key0", "value10");
twoDimensionalMap.get(1).put("key1", "value11");
}
public Set<String> getColumnNames()
{
return twoDimensionalMap.size() > 0 ? twoDimensionalMap.get(0).keySet() : new HashSet<String>();
}
public List<HashMap<String, String>> getTwoDimensionalMap() {
return twoDimensionalMap;
}
}
I'm using a <rich:datatable> to show the content of a List<Map<String, String>
In the code below, spotlightPtBindings is the List<Map<String, String> and spotlightbinding represents each Map<String, String>. In the first column, I'm showing one selectBooleanCheckBox for eah row. When a selectBooleanCheckBox is checked, I'd like to send the value of the Map<String, String> corresponding to the key "URI" as a parameter to the method: inserirBean.onSelectedCheckBox(uri), and that's why I put this value in a ui:param of name: uri. The problem here is that when I try to print the value uri received in inserirBean.onSelectedCheckBox(uri), I don't get the any output, as if it is empty. Below there's the rest of the code:
InsereDocumento.xhtml
<rich:dataTable value="#{inserirBean.spotlightPtBindings}" var="spotlightbinding">
<rich:column>
<f:facet name="header">*</f:facet>
<ui:param name="uri" value="#{spotlightbinding['URI']}"/>
<h:selectBooleanCheckbox value="#{selectionBean.selected}" />
<c:if test="#{selectionBean.selected}">
#{inserirBean.onSelectedCheckBox(uri)}"
</c:if>
</rich:column>
<c:forEach items="#{inserirBean.variableNamesPt}" var="vname">
<rich:column>
<f:facet name="header">#{vname}</f:facet>
#{spotlightbinding[vname]}
</rich:column>
</c:forEach>
</rich:dataTable> <br />
SelectionBean
package managedBeans;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
#ManagedBean
public class CheckBoxSelectionBean implements Serializable {
private transient boolean selected = false;
private static final long serialVersionUID = 1L;
public CheckBoxSelectionBean() {
// TODO Auto-generated constructor stub
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
InserirBean - I'm not showing here how the List<Map<String, String>> named spotlightPtBinding and how the List<String> variableNamesPt were populated, because it was a complex process, but I can tell you they are certainly populated, cause I can see their content on the screen.
#ManagedBean
public class InsereDocumentoBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<String> variableNamesPt = new ArrayList<String>();
private List<Map<String, String>> spotlightPtBindings = new ArrayList<Map<String, String>>();
public List<String> getVariableNamesPt() {
return variableNamesPt;
}
public List<Map<String, String>> getSpotlightPtBindings() {
return this.spotlightPtBindings;
}
public void onSelectedCheckBox(String uri) {
System.out.println("URI: " + uri);
}
}
What may the problem be? Thank you! I'm new to JSF and need your help!
In JSF rendering is a two-step process: there's view build time and view render time.
Although they're in the same file, some tags take effect at view build time, some at render time.
JSTL tags like <c:forEach>, <c:if> and all tag handlers (including <ui:param>, see here) are evaluated at view build time, they add content to the "final" xml tree that is then rendered by JSF.
JSF HTML tags and derivates like <rich:dataTable> are evaluated at view render time, so the datatable's var is evaluated later then the <ui:param> which causes spotlightbinding not to be bound when it's assigned to uri.
Instead, I suggest you assign an ajax listener to call the function:
<h:selectBooleanCheckbox value="#{selectionBean.selected}">
<f:ajax listener="#{inserirBean.onSelectedCheckBox(spotlightbinding['URI'])}" />
</h:selectBooleanCheckbox>
Note that the listener is called whenever the value changes.
I used data table (OpenFaces) for selecting a row in data table using the attribute o:checkboxColumn rowDatas this will filter the selected rows only.
<o:checkboxColumn rowDatas="#{tBean.srInventoryList}">
<f:facet name="header">
<o:selectAllCheckbox />
</f:facet>
</o:checkboxColumn>
When I click a button it displays all the list, but I want only other rows which are not selected whether any attributes for filter the row list.
Actually it's not possible by the API of checkboxColumn by itself, You need to add some additional logic inside your bean for example:
<o:dataTable id="bookMultipleSelection"
var="book"
value="#{BookList.books}">
<o:multipleRowSelection rowDatas="#{BookList.list}"/>
<o:selectionColumn>
<f:facet name="header">
<o:selectAllCheckbox/>
</f:facet>
</o:selectionColumn>
/**other columns here**/
</o:dataTable>
<o:commandButton execute="bookMultipleSelection" action="#{BookList.updateList}" render="bookMultipleSelection" value="Click ME"/>
End something like this in your backing bean :
private List<Book> books;
private List list = new ArrayList();
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public List<Book> getBooks() {
return books;
}
public void updateList(){
books.removeAll(list);
}
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}"/>
I've the following code in my edit user screen
<h:selectManyCheckbox id="selectedGroups" value="#{usersController.selectedGroups}">
<f:selectItems value="#{usersController.groupsList}" var="item" itemLabel="#{item.groupname}" itemValue="#{item.groupid}" />
</h:selectManyCheckbox>
I've user groups list with all the groups in it, and I've selectedGroups list with the groups that are enabled for the user. But, on the edit screen, they are not showing selected by default. What am I missing? Is this not the right way to bind selected many checkboxes?
An item value of the groupsList will be preselected only when equals() method has returned true for at least one item in the selectedGroups.
Assuming that groupid is a Long, then the selectedGroups should return a List<Long> containing the values to be preselected.
Following code will work perfectly for request scope bean...
xhml code....
<h:selectManyCheckbox binding="#{Page1.chk_1}">
<f:selectItem binding="#{Page1.chk_1_options}"/>
</h:selectManyCheckbox>
Java code....
HtmlSelectManyCheckbox chk_1 = new HtmlSelectManyCheckbox();
UISelectItems chk_1_options = new UISelectItems();
public HtmlSelectManyCheckbox getChk_1() {
return chk_1;
}
public void setChk_1(HtmlSelectManyCheckbox chk_1) {
this.chk_1 = chk_1;
}
public UISelectItems getChk_1_options() {
if (chk_1_options.getValue() == null) {
List<SelectItem> lst_chk_options = new ArrayList<SelectItem>();
lst_chk_options.add(new SelectItem(1, "Label1"));
lst_chk_options.add(new SelectItem(2, "Label2"));
chk_1_options.setValue(lst_chk_options);
}
return chk_1_options;
}
public void setChk_1_options(UISelectItems chk_1_options) {
this.chk_1_options = chk_1_options;
}
If you want for session scope then reply, because binding elements in session scope giving problems in some cases...