I have a datatable in which first component is delete button and last is checkbox.
Now problem is that when I delete a row it reset the checkbox selected in other row
I dont want to uncheck the selected checkbox in other row.
<h:dataTable id="languageTableBody" value="#{countryBean.countryLanguageList}" var="countryLangObj" >
<h:column>
<f:facet name="header">#{msg['country.language.label.delete']}</f:facet>
<p:commandLink action="#{countryBean.deleteRow}" immediate="true" update="#form" process="#this">
<h:graphicImage value="../images/delete.png" />
<f:setPropertyActionListener target="#{countryBean.langId}" value="#{countryLangObj.language.languageCode}" />
</p:commandLink>
</h:column>
<h:column>
<f:facet name="header">#{msg['country.language.label.assignlanguage']}</f:facet>
<h:inputText readonly="true" value="#{countryLangObj.language.languageName}" id="languageName" />
</h:column>
<h:column>
<f:facet name="header">#{msg['country.language.label.languagecode']}</f:facet>
<h:inputText readonly="true" value="#{countryLangObj.language.languageCode}" />
</h:column>
<h:column>
<f:facet name="header">#{msg['country.language.label.defaultlanguage']}</f:facet>
<h:selectBooleanCheckbox id="checkBox" value="#{countryLangObj.isDefaultLanguage}" onclick="dataTableSelectOneRadio(this)" />
</h:column>
</h:dataTable>
countryBean.java
public String deleteRow(){
System.out.println("deleteRow()::Enter");
String delLangId = getLangId();
System.out.println("getLangId(): "+getLangId());
if(null != delLangId && delLangId.trim().length() > 0){
System.out.println("Delete Language Code: "+delLangId);
List<CountryLanguageDTO> countryLangList = getCountryLanguageList();
System.out.println("countryLangList: "+ (null == countryLangList));
List<CountryLanguageDTO> tempCountryLangList = new ArrayList<CountryLanguageDTO>();
for (CountryLanguageDTO countryLanguage : countryLangList) {
System.out.println("wewewewew: "+delLangId.equalsIgnoreCase(countryLanguage.getLanguage().getLanguageCode()));
if(!delLangId.equalsIgnoreCase(countryLanguage.getLanguage().getLanguageCode())){
tempCountryLangList.add(countryLanguage);
}
}
setCountryLanguageList(tempCountryLangList);
}
return SUCCESS;
}
addCountry.js
function dataTableSelectOneRadio(radio) {
var id = radio.name.substring(radio.name.lastIndexOf(':'));
var el = radio.form.elements;
for (var i = 0; i < el.length; i++) {
if (el[i].name.substring(el[i].name.lastIndexOf(':')) == id) {
el[i].checked = false;
}
}
radio.checked = true;
}
It happens because of this:
<p:commandLink action="#{countryBean.deleteRow}" immediate="true" update="#form" process="#this">
When clicking the button, you will transmit the form with the checkboxes. But because of process="#this" you will not convert/validate/store any inputs. The only thing that will get processed is the button itself. After that, because of update="#form" the whole form (including the table with the checkboxes) will be re-rendered. But with the old values.
Thus the solution is to either change process="#this" to #form or to add Ajax functionality to store the new values of each checkbox every time the value changes.
It could be a problem with bean scope. If scenario is like:
Precondition:
-Table are just showed.
Scenario:
-Select some checkbox.
-Delete row
Current result
-Selected checkbox is unchecked.
So after delete row you propably reinit view so all data get default state.
Try to change your scope to View(CDI) or Session.
Related
Im trying to implement a JSF-Webapp with Primefaces. In that case i need a Dropdown with a Datatable inside.
The datatable contains over 30.000 entries (3 columns). I already implemented a datatable with virtual scrolling that works fine (i still need to think about initial data loading, maybe its possible to load data only from db, if end of view is reached).
Idea is a dropdown with a String as value (generated from database) and after clicking on the dropdown, you see the datatable, where the user can choose and select one. The selected should be written into database after submit.
<p:selectOneMenu>
<f:selectItems>
<p:dataTable var="entry" value="#{scrollView.lazyModel}"
scrollRows="20" scrollable="true" virtualScroll="true"
scrollHeight="200" lazy="true" rows="40" style="margin-bottom:0">
<p:column headerText="e1" footerText="e1">
<h:outputText value="#{entry.e1}" />
</p:column>
<p:column headerText="e2" footerText="e2">
<h:outputText value="#{entry.e2}" />
</p:column>
<p:column headerText="e3" footerText="e3">
<h:outputText value="#{entry.e3}" />
</p:column>
</p:dataTable>
</f:selectItems>
</p:selectOneMenu>
EDIT: I found a solution to get columns in my dropdown (thx!), but if i solve it this way, i cant use virtual scrolling. My problem is, that i dont know how to check, if scrollbar is at bottom of the dropdown. My idea was to check and if true reload the selectonemenu with new data.
XHTML:
<p:selectOneMenu id="test"
value="#{bean.dto == null ? '': bean.dto}"
var="d">
<f:selectItem
itemLabel="#{bean.dto == null ? '': bean.dto}" />
<f:selectItems value="#{docScroll.currentData}" var="doc" itemValue="#{doc}"
itemLabel="#{doc.forename} #{doc.surname}, #{doc.city}" />
<p:column>
#{d.surname}
</p:column>
<p:column>
#{d.forename}
</p:column>
<p:column>
#{d.city}
</p:column>
<f:ajax event="change" onevent="#{scripts.checkScrollBottom}" listener="#{docScroll.reloadData}" update="test"/>
</p:selectOneMenu>
reloadData:
public void reloadData() {
if (pos == data.size() - 1) {
return;
}
pos += range - 1;
if (pos >= data.size()) {
pos = data.size() - 1;
}
currentData = data.subList(0, pos);
}
checkScrollBottom:
function checkScrollBottom(event){
var source = event.target;
if(source.offsetHeight + source.scrollTop == source.scrollHeight){
alter("END!!");
}
}
I'm trying to filter a p:dataTable with Primefaces component p:selectCheckboxMenu in one column header. This however, does not work as intended. The other filters on the datatable work just fine, such as input fields.
The column in question is the Room type, that has the p:selectCheckboxMenu.
The filtering works once, after that checking or unchecking boxes on the selectCheckbox menu doesn't add or remove any filtering on the table.
Here's an interesting bit on the problem:
If I remove the selectionMode="single" attribute from the datatable, then the sorting works even after the first checkBox toggle. As in, I can toggle and untoggle a box and the p:dataTable gets filtered accordingly. But, I need the selection mode here, since I'm supposed to be able to select a row and navigate to another view by clicking it. That doesn't work when there's no selectionMode attribute on the datatable.
Here's my datatable:
<div class="background">
<div class="freeRoomsContent">
<br/>
<p:outputLabel value="free rooms" styleClass="headerfont"/>
<br/>
<h:form id="freeRoomsForm">
<p:dataTable id="freeRoomsTable" var="room" paginatorPosition="bottom" paginatorAlwaysVisible="false"
value="#{freeRoomsController.freeRoomsList}" selectionMode="single" selection="#{freeRoomsController.room}"
rowKey="#{room.roomId}" widgetVar="freeRoomsTable"
paginator="true" rows="20" pageLinks="5" scrollable="false"
paginatorTemplate="{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="20,50,100" skipChildren="true" emptyMessage="No free rooms available.">
<p:ajax event="rowSelect" listener="#{freeRoomsController.onRowSelect}" />
<p:column headerText="Room Id" sortBy="#{room.roomId}" filterMatchMode="contains" filterBy="#{room.roomId}">
<h:outputText value="#{room.roomId}"/>
</p:column>
<p:column headerText="Room number" sortBy="#{room.roomNumber}" filterMatchMode="contains" filterBy="#{room.roomNumber}">
<h:outputText value="#{room.roomNumber}" />
</p:column>
<!-- other similar columns -->
<p:column headerText="Room type" filterMatchMode="exact" filterBy="#{room.roomType}">
<f:facet name="filter">
<p:selectCheckboxMenu onchange="PF('freeRoomsTable').filter()"
label="Room type">
<f:selectItems value="#{staticData.roomTypes}" var="rt" itemLabel="#{msg[rt.name]}" itemValue="#{rt.name}"
/>
<p:ajax event="change" process="#this" update="freeRoomsForm" />
<p:ajax event="toggleSelect" process="#this" update="freeRoomsForm" />
</p:selectCheckboxMenu>
</f:facet>
<h:outputText value="#{msg[room.roomtype.name]}">
<f:convertDateTime pattern="dd.MM.yyyy" />
</h:outputText>
</p:column>
<!-- normal input field columns that work -->
</p:dataTable>
</h:form>
</div>
</div>
This worked for me:
Using value and filteredValue in p:dataTable
Use only onchange=PF('freeRoomsTable').filter(), without child p:ajax elements in p:selectCheckboxMenu
Set value attribute for p:selectCheckboxMenu, e.g. value="#{someBean.selectedItems}"
In your bean, use private String[] selectedItems, with getter and setter
In your bean implement filterFunction, method need to have signature boolean (Object, Object, Locale) altough for this example only 1st argument (Object value) is used:
public boolean filterFunction(Object value, Object filter, Locale locale) {
// instanceof checking probably not needed
if (value == null || !(value instanceof String)) {
return true;
}
String valueInRow = (String)value;
// if nothing is selected, show row in table i.e. return true,
// you can play with this ofcourse
if (selectedItems == null || selectedItems.length == 0) {
return true;
}
// if item in row matches any of the items that were selected in header,
// show in table i.e. return true
for (int i = 0; i < selectedItems.length; i++) {
if (selectedItems[i].equals(valueInRow)) {
return true;
}
}
// if you don't want to show row in table, return false
return false;
}
In p:column, call filterfunction=#{someBean.filterFunction} - no arguments, no parenthesis, no need to use any filterMatchMode, leave only filterBy
I have a datatable that is initially populated with no data (an empty list). There is a commandButton in the footer of the table that adds rows to the table. When a row is added, the last cell in the row is another commandButton to delete the row. My issue is that the delete button keeps deleting the first row in the table regardless of which row is selected.
Example: if I add 3 rows to the table and hit the delete button in the last cell of the second row, action="#{addAccountBean.deleteFixVendor(dataItem)}" passes the first row to the deleteFixVendor method instead of the second row which was clicked. I can't figure out why the object for the first row keeps on getting passed to the delete method regardless of which row button is clicked.
My JSF code:
<h:form>
<f:validateBean disabled="#{!empty param['disableValidation']}" >
<p:panel id="panel" header="Add New Account" >
<p:dataTable var="dataItem" value="#{addAccountBean.account.vendors}" >
<p:column headerText="Vendor" >
<p:selectOneMenu value="#{dataItem.vendor}">
<f:selectItems value="#{addAccountBean.menu.fixOption}" />
</p:selectOneMenu>
</p:column>
<p:column headerText="Monthly Cost" >
<p:inputText value="#{dataItem.cost}" />
</p:column>
<p:column headerText="Cost Date" >
<p:calendar value="#{dataItem.CDate}" pattern="MM/dd/yyyy" navigator="true" />
</p:column>
<p:column headerText="Delete" >
<h:commandButton value="Delete" action="#{addAccountBean.deleteFixVendor(dataItem)}" >
<f:param name="disableValidation" value="true" />
</h:commandButton>
</p:column>
<f:facet name="footer" >
<h:commandButton value="Add New Vendor" action="#{addAccountBean.addFixVendor}" >
<f:param name="disableValidation" value="true" />
</h:commandButton>
</f:facet>
</p:dataTable>
</p:panel>
</f:validateBean>
</h:form>
Bean code:
#ManagedBean
#SessionScoped
public class AddAccountBean implements Serializable {
private AccountData account = new AccountData();
public String addFixVendor() {
account.getVendors().add(new VendorData());
return "";
}
public String deleteFixVendor(VendorData vData) {
account.getVendors().remove(vData);
return "";
}
Any suggestions are appreciated.
The code posted so far looks fine. The only feasible explanation for this problem is a broken equals() method in the VendorData class. The Collection#remove() will remove the first item in the collection which equals() the given object.
Provided that your (base) entity has an id property, then this should do:
#Override
public boolean equals(Object other) {
return (other != null && getClass() == other.getClass() && id != null)
? id.equals(((VendorData) other).id)
: (other == this);
}
#Override
public int hashCode() {
return (id != null)
? (getClass().hashCode() + id.hashCode())
: super.hashCode();
}
as per my requirement I have to show a delete button end of row in datattable but restriction is to show only for first row, not in each row.
how to restrict it
belwo is my datatable
<h:dataTable id="datatable" value="#{relationBean.languageDTOList}" var="lang">
<h:column>
<f:facet name="header"> Relation Type Name</f:facet>
<h:outputText value="#{relationBean.relationName}" />
</h:column>
<h:column>
<f:facet name="header"> Value</f:facet>
<h:inputText />
</h:column>
<h:column>
<f:facet name="header">language</f:facet>
<h:outputText value="#{lang.languageName}" />
</h:column>
<h:column>
<f:facet name="header"> Delete</f:facet>
<p:commandLink rendered="" action="#{relationBean.deleteDataTable}" immediate="true" update="#form" process="#this">
<h:graphicImage value="../images/delete.png" />
<f:setPropertyActionListener target="#{relationBean.deleteId}" value="#{var}" />
</p:commandLink>
</h:column>
Bean.java its showing contents of bean
public List<LanguageDTO> getLanguageDTOList() {
System.out.println("RelationBean:getLanguageDTOList:Enter");
CountryList = new ArrayList<Country>();
try {
CountryList.add(countryService.getCountryByCode(city));
List<LanguageDTO> tempLangDTOlst=new ArrayList<LanguageDTO>();
for (Country lang : CountryList) {
LanguageDTO languageDTO = new LanguageDTO();
for (CountryLanguage CLang : lang.getCountryLanguage()) {
languageDTO = new LanguageDTO();
languageDTO.setLanguageName(CLang.getLanguage().getLanguageName());
languageDTO.setLanguageCode(CLang.getLanguage().getLanguageCode());
System.out.println(CLang.getLanguage().getLanguageName());
tempLangDTOlst.add(languageDTO);
}
}
setLanguageDTOList(tempLangDTOlst);
System.out.println("languageDTOList :"+languageDTOList);
} catch (Exception e) {
e.printStackTrace();
}
//BeanUtils.copyProperties(tempLangDTO,languageDTOList);
System.out.println("--------------------Start -------------");
for (LanguageDTO iterable_element : languageDTOList) {
System.out.println(iterable_element.getLanguageName());
}
System.out.println("-------------------- End -------------");
System.out.println("RelationBean:getLanguageDTOList:Exit");
return languageDTOList;
}
Bind the <h:dataTable> to the view via binding, which will reference an UIData component instance and in the rendered attribute just check if UIData#getRowIndex() equals to 0.
<h:dataTable binding="#{table}" ...>
<h:column>
<p:commandLink ... rendered="#{table.rowIndex eq 0}">
(note: do not bind it to a bean like #{bean.table}! the code is complete as-is)
See also:
How does the 'binding' attribute work in JSF? When and how should it be used?
JSF component binding without bean property
You can test if the current element is the same as the first element of your list:
<p:commandLink rendered="#{lang eq relationBean.languageDTOList.get(0)}"> ...
This would works only if languageDTOList is a List.
If you were using p:dataTable instead of the standard JSF DataTable, you could use the rowIndexVar variable to test if your are at the first iteration.
I have a h:datatable showing a list of rows, and the fields of each row are input fields.
I render an "Add Row" button before the table, and a "Remove Row" button on each row of the table.
The baking bean is viewScoped, and the buttons add/remove elements from the java list in the backing bean, and then return to the same view.
I set the immediate attribute to "true" in the buttons in order to not validate the input fields when I add or remove a row.
Everything works ok but one thing: the values of the input fileds are cleared. I thought that the view kept the values beacuse the bean is viewScoped.
How can I achieve adding/removing rows without triggering validations and keeping the values that were already typed by the user in the form?
My view:
<h:form>
<h:commandButton value="Añadir Fila" immediate="true" action="#{tablaController.addRowAction}" />
<h:dataTable value="#{tablaController.lista}" var="fila" cellpadding="0" cellspacing="0" border="1">
<f:facet name="header">TABLA</f:facet>
<h:column>
<f:facet name="header"><h:outputLabel value="NOMBRE" /></f:facet>
<h:inputText id="nom" value="#{fila.nombre}" />
<h:message for="nom" class="msjError" />
</h:column>
<h:column>
<f:facet name="header"></f:facet>
<h:commandButton value="Quitar Fila" immediate="true" action="#{tablaController.removeRowAction(fila)}" />
</h:column>
</h:dataTable>
</h:form>
My backing bean:
#ManagedBean(name="tablaController")
#ViewScoped
public class TablaController {
private List<Fila> lista;
...
public TablaController() { }
...
#PostConstruct
public void init() {
this.lista = new ArrayList<Fila>();
for (int i=0; i<5; i++) {
Fila fila = new Fila();
fila.setNombre("");
this.lista.add(i,fila);
}
}
...
public String addRowAction () {
Fila fila = new Fila();
fila.setNombre("");
this.lista.add(fila);
return "";
}
public String removeRowAction (Fila f) {
boolean exito = this.lista.remove(f);
return "";
}
...
}
UPDATE --> MY SOLUTION:
I write here my solution if someone is interested.
The problem is that I use immediate="true" to skip validations, but this makes to skip the update_model_values too, so that the values entered by the user in the form are lost after clicking the add/remove buttons and re-redenring the page.
As I use "JSR-303 bean validation", my solution was to skip validations using the f:validateBean to enable/disable them. Depending on the button I click, if I want the validations to execute, I enable the bean validation (for example in a "submit" button), and if I want to skip them, I disable bean validation (like in the add/remove row buttons). But anyway the update_model_values always executes, so the values are not lost.
Here's the view:
<h:form>
<f:validateBean disabled="#{!empty param['disableValidation']}">
<h:commandButton value="Añadir Fila" action="#{tablaController.addRowAction}">
<f:param name="disableValidation" value="true" />
</h:commandButton>
<h:dataTable value="#{tablaController.lista}" var="fila" cellpadding="0" cellspacing="0" border="1">
<f:facet name="header">TABLA</f:facet>
<h:column>
<f:facet name="header"><h:outputLabel value="NOMBRE" /></f:facet>
<h:inputText id="nom" value="#{fila.nombre}" />
<h:message for="nom" class="msjError" />
</h:column>
<h:column>
<f:facet name="header"></f:facet>
<h:commandButton value="Quitar Fila" action="#{tablaController.removeRowAction(fila)}">
<f:param name="disableValidation" value="true" />
</h:commandButton>
</h:column>
</h:dataTable>
<h:commandButton value="Submit" action="#{tablaController.saveData}" />
</f:validateBean>
</h:form>
The backing bean:
#ManagedBean(name="tablaController")
#ViewScoped
public class TablaController {
private List<Fila> lista;
...
public TablaController() { }
...
#PostConstruct
public void init() {
this.lista = new ArrayList<Fila>();
for (int i=0; i<5; i++) {
Fila fila = new Fila();
fila.setNombre("fila "+i);
this.lista.add(i,fila);
}
}
...
public String addRowAction () {
Fila fila = new Fila();
fila.setNombre("");
this.lista.add(fila);
return "";
}
public String removeRowAction (Fila f) {
this.lista.remove(f);
return "";
}
...
public String saveData () {
...
//processes the valid data
//for example, calls to a service method to store them in a database
...
return "";
}
...
}
I set the immediate attribute to "true" in the buttons in order to not validate the input fields when I add or remove a row.
immediate="true" is the wrong tool for the job. It should be used to prioritize validation, not to enable/disable validation. The difference is rather huge as you encountered yourself.
You want to trigger validation conditionally. In case of e.g. required="true" that'd be as easy as
<h:inputText ... required="#{saveButtonPressed}" />
where #{saveButtonPressed} evaluates true when the save button is pressed. E.g. when its client ID is present in request parameter map.
In case of JSR 303 bean validation, that'd be a matter of
<f:validateBean disabled="#{not saveButtonPressed}">
<h:inputText ... />
</f:validateBean>
or with OmniFaces <o:validateBean> which allows controlling that on a per-command basis.
<h:commandButton id="add" ...>
<o:validateBean disabled="true" />
</h:commandButton>
I had exactly the same problem. In short, you can NOT use immediate for action that update data table(UIData) or facelet repeat. Short explanation:submitted values are not kept for re-display if inputs in UIData do not go through validation. Long explanation can be found here: long explanation and a related bug in Mojarra