I don't seem to get multiple selection in PrimeFaces dataTables working.
I'm trying to implement a list of clients (dataList) and show their respective bookings in nested dataTables with a possibility to select multiple bookings for billing:
<p:dataList value="#{clientController.allClients}" var="client">
<p:column>
<p:dataTable value='#{client.bookingsDataModel}' var='item' selection="#{client.bookingsToBill}">
<p:column selectionMode="multiple" />
</p:dataTable>
</p:column>
</p:dataList>
My controller and backing bean classes:
public class ClientController {
public List<Client> getAllClients() {
return clients;
}
}
public class Client {
private List<Booking> bookings;
private Booking[] bookingsToBill;
public LeistungDataModel getBookingsDataModel() {
return new BookingsDataModel(bookings);
}
public Booking[] getBookingsToBill() {
return bookingsToBill;
}
public void setBookingsToBill(Booking[] bookingsToBill) {
this.bookingsToBill = bookingsToBill;
}
}
The data model class:
public class BookingsDataModel extends ListDataModel<Booking> implements SelectableDataModel<Booking> {
public BookingsDataModel(List<Booking> data) {
super(data);
}
#Override
public Booking getRowData(String rowKey) {
List<Booking> bookings = (List<Booking>) getWrappedData();
for(Booking booking : bookings) {
if(("booking_"+booking.getId().toString()).equals(rowKey)) {
return booking;
}
}
return null;
}
#Override
public Object getRowKey(Booking booking) {
return "booking_"+booking.getId().toString();
}
}
The browser posts the following data to the server, when I submit the form with my selections:
j_idt9%3Aj_idt13%3A0%3Aj_idt15_selection:booking_300,booking_301,booking_302
j_idt9%3Aj_idt13%3A1%3Aj_idt15_selection:booking_566,booking_567
j_idt9%3Aj_idt13%3A2%3Aj_idt15_selection:
Also, I found during debugging that the getRowData method of the BookingsDataModel returns the correct Booking objects (the selected ones).
However, always empty arrays are passed to the setBookingsToBill of my Client objects. What could be going wrong here?
Update:
An empty array is only passed the first Client objects - it doesn't matter if a booking has been selected or not. All other Client objects' setBookingsToBill methods are called with a parameter value of null.
Not really, if you want multiple selection with check box you have to do as jfs did:
In the showcase there is one example showing just that. It will create a column containing the boxes for the user to select. You can also do as you've said, using an attribute of p:dataTable, however this will not create the boxes and the user will have to control+click to do multiple select.
The selectionMode should be a part of the <p:dataTable> tag.
Here is a link to the showcase which has an example.
http://www.primefaces.org/showcase/ui/datatableRowSelectionMultiple.jsf
Related
I need your help in disabling and enabling an item from the selectManyCheckbox component in a jsf page. First of all, the selectManyCheckbox component is showing three chechboxes which are (Loan - Health - Transfer). The list will be populated from a bean which it has the code:
private List<hrCertificate> hrCertificatesList = new ArrayList<hrCertificate>();
//Getter and Setter
Private String loanFlag="";
#PostConstruct
public void init() {
this.hrCertificatesList.add(new hrCertificate(("Loan"), "LC"));
this.hrCertificatesList.add(new hrCertificate(("Health"), "HI"));
this.hrCertificatesList.add(new hrCertificate(("Trasnfer"), "TE"));
}
In the same bean, I will be running a SQL statement that will return either Yes or No and that value I am adding it to the loanFlag variable.So if the flag="Y", I need to enable the loan checkbox so the user can select it else I need to disable it from the selectManyCheckbox. The issue is that I am facing difficulties in applying the logic to disable and to enable the item selectManyCheckboxwhere in the above code I am listing and enabling them all the time.
The code for the selectManyChexkbox:
<p:selectManyCheckbox id="hrCertificates" value="#{user.selectedHRCertificates}" layout="pageDirectio>
<f:selectItems value="#{user.hrCertificatesList}"
var="hrCertificate" itemLabel="#{hrCertificate.hrCertificateName}"
itemValue="#{hrCertificate.hrCertificateCode}"/>
</p:selectManyCheckbox>
So how to apply the logic
Could you edit your hrCertificate class to add a disabled boolean field? If yes, then you can add itemDisabled="#{hrCerticate.disabled}" to your f:selectItems which should be the easiest solution.
Another option would be to use a Map<hrCertificate, Boolean> instead of a List<hrCertificate>.
private Map<hrCertificate, Boolean> hrCertificatesMap = new HashMap<hrCertificate, Boolean>();
#PostConstruct
public void init() {
hrCertificatesMap.put(new hrCertificate(("Loan"), "LC"), null);
hrCertificatesMap.put(new hrCertificate(("Health"), "HI"), null);
hrCertificatesMap.put(new hrCertificate(("Trasnfer"), "TE"), null);
}
// Then when you're done with your SQL query, update your Map to add the corresponding boolean values...
.xhtml
<p:selectManyCheckbox id="hrCertificates" value="#{user.selectedHRCertificates}" layout="pageDirectio>
<f:selectItems value="#{user.hrCertificatesMap.keySet().toArray()}" var="hrCertificate" itemLabel="#{hrCertificate.hrCertificateName}" itemValue="#{hrCertificate.hrCertificateCode}" itemDisabled="#{user.hrCertificatesMap.get(hrCertificate)}" />
</p:selectManyCheckbox>
First, note that a property does not retire an actual attribute backing it, you only need a getter. So you can have:
public class MyBean implements Serializable {
private FilterEnum certFilter = FilterEnum.NO_FILTER;
private List<Certificate> certificates;
... // including certificates initialization.
public FilterEnum getCertFilter() {
return this.certFilter;
}
public void setCertFilter(FilterEnum certFilter) {
this.certFilter = certFilter;
}
public List<Certificate> getCertificates() {
// I am sure there is a cooler way to do the same with streams in Java 8
ArrayList<Certificate> returnValue = new ArrayList<>();
for (Certificate certificate : this.certificates) {
switch (this.certFilter) {
case FilterEnum.NO_FILTER:
returnValue.add(certificate);
break;
case FilterEnum.ONLY_YES:
if (certificate.isLoan) {
returnValue.add(certificate);
}
break;
case FilterEnum.ONLY_NO:
if (!certificate.isLoan) {
returnValue.add(certificate);
}
break;
}
}
return returnValue;
}
}
If you insist that you want to do the filter "in the .xhtml", you can combine c:forEach from JSTL with <f:selectItem> (note item, not items), but it will make your xhtml more complicated and may cause issues if you want to use Ajax with it.
Hi i am new to JSF & Primefaces. I've been stuck for two days trying to persist dynamically selected check-values to DB.
I'm successfully displaying check-boxes using labels from DB using 3 tables:
First table: DEM_checkbox which contains checkbox ID, label
Second table: DEM_checkboxval which contains fileid, checkbox_id, value
Third table: DEM_folder where all files save in table format in database
I've used separate mappers, services and classes for table.
This is my xhtml code:
<ui:repeat value="dossierBean.dossier.dui.demCheckBoxList" var="c">
<h:panelGrid columns="2" columnClasses="LeftColumn,LeftColumn">
<h:selectBooleanCheckbox value="#{c.checkboxlabel}" styleClass="benefSelection" />
<h:outputText value="#{c}" styleClass="subSectionTitle" />
</h:panelGrid>
</ui:repeat>
I'm using this loop to save selected values:
public List<DEMCheckBoxUI> getDemCheckBoxList() {
if (demCheckBoxList==null){
demCheckBoxList = new ArrayList<DEMCheckBoxUI>();
for (DEMCheckboxes cblabel :dossier.getDemCheckBoxList()){
DEMCheckBoxUI cbui = new DEMCheckBoxUI();
cbui.setLabel(cblabel);
List<String> valueList = services.getDemCheckboxValueService().getCheckboxValue(dossier.getId(), cblabel.getCheckboxlabel());
DEMCheckboxesValue cbvalue = new DEMCheckboxesValue();
cbvalue.setDossier(dossier.getId());
cbvalue.setId(cblabel.getId());
cbvalue.setValue(valueList.get(0));
cbui.setCboxvalue(cbvalue);
demCheckBoxList.add(cbui);
}
}
return demCheckBoxList;
}
Labels are retrieved from DB via DENCheckboxes.java
Saved values are inserting via DEMCheckboxesVal
DemcheckboxUI is UI code looks like:
public class DEMCheckBoxUI {
private DEMCheckboxes label;
private DEMCheckboxesValue cboxvalue;
public DEMCheckboxesValue getCboxvalue() {
return cboxvalue;
}
public void setCboxvalue(DEMCheckboxesValue cboxvalue) {
this.cboxvalue = cboxvalue;
}
}
To save to database i am using the below code:
demCheckboxService = DossierManager.getCurrent().getDemCheckboxService();
if (dossier != null) {
dossier.setDemCheckBoxList(demCheckboxService.getCheckboxesList());
if (PgzTools.notEmpty(dossier.getId())) {
reloadDossier(dossier.getId());
}
}
I think there is a problem in the loop.
It would be help if anyone give suggestion or other method to fix.
I have looked around and could not find a solution.
I am using Omnifaces listConverter in the PickList component of Primefaces. When I move an item from SOURCE to TARGET. In the backing bean i get the new item ONLY on dualList.getTarget().
However when I move an item from TARGET to SOURCE. In the backing bean I am not able to check whether an item has been removed from the TARGET.
I tried dualList.getSource() - SOURCE does not contains the item/s dropped from TARGET
And off course dualList.getTarget() will be empty (assuming that no item is moved from SOURCE to TARGET).
My question is how can i find that something has been moved from TARGET to SOURCE in the backing bean?
Previously I have created a custom converter and in that, my dualList.getTarget() gives me the updated target (ie old values + new values added + old values removed). Therefore I can figure it out which values are added, removed and are still present.
But while using Omnifaces listConverter i am not understanding how to achieve it.
My code looks like this:
XHTML
<p:pickList id="pickList" value="#{assignBenchmarkersBean.dualUserList}"
var="users" itemLabel="#{users.username}" itemValue="#{users}" >
<o:converter converterId="omnifaces.ListConverter"
list="#{assignBenchmarkersBean.dualUserList.source}" />
</p:pickList>
<p:commandButton id="update" value="#{bundle.create}"
styleClass="redButton bigFont"
actionListener="#{assignBenchmarkersBean.update()}" />
Bean
public void update(){
try{
prepareRemoveOldAndAddNewUsersList();
/**some other code **/
}catch(Exception e){
FacesMsg.error(ErrorMessage.UPDATE.getValue(), e);
}
}
private void prepareRemoveOldAndAddNewUsersList() {
for(User user : dualUserList.getTarget()){ //add users
addUsers.add(user);
}
for(User user : dualUserList.getSource()){ //remove users
if(oldUserList.contains(user)){
removeUsers.add(user);
}
}
}
User entity
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof User)) {
return false;
}
User other = (User) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.abc.model.main.User[ id=" + id + " ]";
}
NOTE:
If in the converter's list attribute, I put bean.dualList.target, then the whole thing works the other way around.
It is better to use ajax transfer event for this goal, as described in PrimeFaces User Guide.
XHTML:
<p:pickList id="pickList" value="#{assignBenchmarkersBean.dualUserList}"
var="users" itemLabel="#{users.username}" itemValue="#{users}" >
<o:converter converterId="omnifaces.ListConverter"
list="#{assignBenchmarkersBean.dualUserList.source}" />
<p:ajax event="transfer" listener="#{pickListBean.handleTransfer}" />
</p:pickList>
Bean:
public class PickListBean {
//DualListModel code
public void handleTransfer(TransferEvent event) {
//event.getItems() : List of items transferred
//event.isAdd() : Is transfer from source to target
//event.isRemove() : Is transfer from target to source
}
}
This is how I got it worked for me.
In the implementation of the Omnifaces ListConverter, the getAsObject method is using the list to find the value and update the target and source of dualList.
NOTE: you will get either a new value added in target (List=".getSource") or a new value added in source (List=".getTarget"), as per the List value you have entered.
Hence by changing the List from myBean.dualList.getSource to myBean.completeList, I get the updated target and source (ie old values + new values added + old values removed ).
If a user is only concerned with the new value added in target or source and not on if any value is removed, then he should use myBean.dualList.getSource and myBean.dualList.getTarget respectively.
However if user wants to know the final content as it is in my case, use the whole collection ie myBean.completeList.
In short it is the List which you are passing in the <o:conveter> tag is important.
Hope it will help!!
I have a database table with some oneToMany relations.
this is fragment of the entity:
#OneToMany(mappedBy="auction")
private List<Biding> bidings;
now i want to print higher bid on my jsf website:
<ui:repeat var="singleAuction" value="#{auctionListBean.auctionList}" varStatus="status">
<h:outputLabel value="#{singleAuction.getHigherBid()}"/>
</ui:repeat>
this my aucListBean
#ManagedBean
public class AuctionListBean
{
#PersistenceContext()
EntityManager entityManager;
public List<AuctionBean> getAuctionList() {
Query query = entityManager.createQuery("SELECT e FROM Auction e");
#SuppressWarnings("unchecked")
List<AuctionBean> resultList = (List<AuctionBean>) query.getResultList();
return resultList;
}
}
I also have AuctionBean class which I used before to add new auction. Now I want to create a bean which has one property: list of auctionBean. I populate it using my entity and cast it to AuctionBean. In AuctionBean class I implemented mentioned method:
public double getHigherBid()
{
double higherBid = 0;
for(Biding a : bidings)
{
if(a.getCurrentPrice() > higherBid)
higherBid = a.getCurrentPrice();
}
return higherBid;
}
The problem is "Method not found". It seems like it still dont even use AuctionBean class for some reason. It may be a problem why it cannot see the method. I am doing it right and where is the problem exactly? Could you help?
I have two suggestions:
1. Use singleAuction.higherBid, not singleAuction.getHigherBid()
2. Specify the target class of your query. Here's an example:
return entityManager.createQuery("SELECT e FROM Auction e", AuctionBean.class)
.getResultList();
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.
}