PrimeFaces Ajax Filter(FilterEvent event) is called after getter for columns Filterfield - jsf

I'm trying to filter a DataTable with multiple filters.
After I set a filter, values for other filter columns may change.
Filter columns are for example
<p:dataTable id="tablePerson"
value="#{beanModel}"
var="actualP"
....
<p:column id="person" filterBy="#{actualP.person}" filterValue="#{model.personFilter}">
<f:facet name="header">
<h:outputText value="#{apptext['person']}" />
</f:facet>
<f:facet name="filter">
<h:selectOneMenu
converter="#{bean.pConverter}"
onchange="PF('tableP').filter()">
<f:selectItem itemLabel="all" itemValue="#{null}" noSelectionOption="true"/>
<f:selectItems value="#{model.persons}" var="p" itemValue ="#{p}" itemLabel="#{p.name}"/>
</h:selectOneMenu>
</f:facet>
</p:column>
....
</p:table>
After setting one of this filter columns, load() of LazyDataModel is called.
Setter for filterValue of column is not called.
I implemented an ajax filter event method:
public void tableFilter(FilterEvent event){
event.getFilterBy()
.values()
.stream()
.filter(filterMeta -> filterMeta.getFilterValue() != null)
.forEach(filterMeta -> System.out.println(filterMeta.getColumnKey() + " " + filterMeta.getFilterValue().toString()));
}
which is called after the getter for next filter column.
Is there any advise on how to get filterValues before getter of filterColumn is called?

Related

Primefaces Dropdown with Datatable

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!!");
}
}

primefaces datatable mutiple selection checkbox: checking at least one item is selected

I have a primefaces datatable with multiple checkbox selection and I can't find a way to check if there is at least one item selected using postValidate event
xhtml
<p:dataTable
var="item"
value="#{myBean.list}"
selection="#{myBean.selectedItems}">
<p:column selectionMode="multiple" />
<p:column>
<f:facet name="header">
<h:outputText value="Item" />
</f:facet>
<h:outputText value="#{item.value}" />
</p:column>
<f:event listener="#{myBean.isSelectedItem}" type="postValidate" />
</p:dataTable>
My Bean
public void isSelectedItem(ComponentSystemEvent event) {
HtmlDataTable table = (HtmlDataTable) event.getComponent();
//no idea how to get checkboxes inside datatable
}
How can I get elements inside datatable ?
I think that you can do it using your back bean method by checking if the selection list "selectedItems" contains values or not.

h:selectOneMenu value should change onclick of p:selectBooleanCheckbox

I have a problem in my application.
I have a datatable, inside that one selectBooleancheckbox , selectonemenu with items("Confirmed", "Rejected" ,"selected") and one inputText is there. I need to write code for,
if selectbooleancheckbox is checked then the selectonemenu will get the value "Selected" and the inputText also become editable.
this is my xhtml code:
<p:dataTable id="panel1"
rendered="true"
var="step2table"
value="#{dtstep2_tab1.step2table}"
editMode="cell"
scrollable="true"
selection="#{dtstep2_tab1.selectedrows}"
rowKey="#{step2table.rs2_empname}"
rowSelectMode="checkbox"
rowStatePreserved="true">
<p:column headerText="Select" id="hSelect" style="font-size:12px;width:60px;">
<p:selectBooleanCheckbox id="Booleanchkbox" >
<p:ajax listener="#{dtstep2_tab1.UpdateStatus}" update="panel1" />
</p:selectBooleanCheckbox>
</p:column>
<p:column headerText="Status" id="hStatus" style="font-size:12px;width:100px;">
<h:selectOneMenu id="cbo" value="#{step2table.rs2_status}" >
<f:selectItem itemLabel="Confirmed" itemValue="Confirmed"></f:selectItem>
<f:selectItem itemLabel="Selected" itemValue="Selected"></f:selectItem>
<f:selectItem itemLabel="Rejected" itemValue="Rejected"></f:selectItem>
</h:selectOneMenu>
</p:column>
<p:column style="font-size:12px;width:150px;">
<f:facet name="header">
<h:outputText value="Comments"></h:outputText>
</f:facet>
<h:inputText value="#{step2table.rs2_comments}" rendered="#{dtstep2_tab1.editable}"/>
</p:column>
</p:dataTable>
and in bean I have the following code in addition with getter and setter method.
public void UpdateStatus(AjaxBehaviorEvent event) {
SelectBooleanCheckbox permit = (SelectBooleanCheckbox) event.getComponent();
boolean checked = (Boolean) permit.getValue();
if (checked) {
setRs2_status("Selected");
setEditable(true);
setSelect(true);
}
else
{
setEditable(false);
setSelect(false);
}
}
I don't know what I am doing wrong here. Please guide me on the issue.

Delete row form Primefaces LazyDataModel

I have a table with a date column that always null. The data is a LazyDataModel child. Also i have row editing.
<p:dataTable id="dataTable" var="Var" value="#{tableBean.model}"
lazy="true"...........
<p:ajax event="rowEdit" listener="#{tableBean.onRowEdit}"
update=":dataTableForm:messages"/>
<p:column sortBy="VarName">
<f:facet name="header">
<h:outputText value="#{msg['Var.table.header.assignee']}"/>
</f:facet>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{Var.Name}"/>
</f:facet>
<f:facet name="input">
<h:inputText id="assigneeNameInput" styleClass="row-input" value="#{Var.Name}"/>
<p:message for="assigneeNameInput"/>
</f:facet>
</p:cellEditor>
</p:column>
<f:facet name="header">
<h:outputText value="#{msg['var.table.header.action']}"/>
</f:facet>
<p:rowEditor/>
</p:column>
I need to delete edited row if date column was filled.
I try this
public void onRowEdit(RowEditEvent event) {
Var updatedVar = (Var) event.getObject();
if (updatedVar.getReturnDate() != null) {
updatedVar = null;
}
}
And this
public void onRowEdit(RowEditEvent event) {
Var updatedVar = (Var) event.getObject();
if (updatedVar.getReturnDate() != null) {
((List<T>) getWrappedData()).remove(oldEntry);
}
}
Both attempts did not work, only if update table twice via remoteCommand . Suggest the decision. Thanks!
Edit: Data removed from lazydatamodel, but on page i still had updated row.
Edit : JSF application lifecycle consist of six phases.
Phase 4:Update model values :
After the JSF checks that the data is valid, it walks over the component tree and set the corresponding server-side object properties to the components' local values. The JSF will update the bean properties corresponding to input component's value attribute.
Phase 5: Invoke application :
During this phase, the JSF handles any application-level events, such as submitting a form / linking to another page.
So, invocation onRowEdit() method occur after dataTable's update.

Populate p:selectOneMenu based on another p:selectOneMenu in each row of a p:dataTable

I have a <p:dataTable> with lazy load. In two of the columns, there is a <p:selectOneMenu> in each of them.
The first column holds a list of countries and the second one holds a list of states from a database.
I want the second menu (the one that contains a list of states) to show only those states in each row of the data table which correspond to the country in the first menu in each row of the data table.
During edit mode, when a country in its menu is changed, the states corresponding to that country should be populated in its menu in that current row.
How to load such lists of states that correspond to their countries in each row of the data table?
These two columns in the data table are left incomplete, since I don't have a precise idea about how to achieve this.
<p:column>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{row.state.country.countryName}"/>
</f:facet>
<f:facet name="input">
<p:selectOneMenu value="#{row.state.country}">
<f:selectItems var="country"
value="#{cityBean.selectedCountries}"
itemLabel="#{country.countryName}"
itemValue="#{country}"/>
<p:ajax update="states" listener="#{cityBean.getStates}"/>
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
<p:column>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{row.state.stateName}"/>
</f:facet>
<f:facet name="input">
<p:selectOneMenu id="states">
<f:selectItems var="state"
value="#{cityBean.selectedStates}"
itemLabel="#{state.stateName}"
itemValue="#{state}"/>
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
cityBean.selectedCountries retrieves all the countries which is necessary but cityBean.selectedStates also retrieves all the states from the database which is unnecessary and should be modified to retrieve only those states which correspond to its country in another menu.
How can I proceed from here?
Whilst your initial solution works, it's in fact inefficient. This approach basically requires the entire object graph of the Country and State tables (even with circular references) to be fully loaded in Java's memory per JSF view or session even though when you simultaneously use only e.g. 5 of 150 countries (and thus theoretically 5 state lists would have been sufficient instead of 150 state lists).
I don't have full insight into your functional and technical requirements. Perhaps you're actually simultaneously using all of those 150 countries. Perhaps you've many pages where all (at least, "many") countries and states are needed. Perhaps you've state of art server hardware with plenty of memory so that all countries and states can effortlessly be duplicated over all JSF views and HTTP sessions in memory.
If that's not the case, then it would be beneficial to not eagerly fetch the state list of every single country (i.e. #OneToMany(fetch=LAZY) should be used on Country#states and State#cities). Given that country and state lists are (likely) static data which changes very few times in a year, at least sufficient to be changed on a per-deploy basis only, it's better to just store them in an application scoped bean which is reused across all views and sessions instead of being duplicated in every JSF view or HTTP session.
Before continuing to the answer, I'd like to remark that there's a logic error in your code. Given the fact that you're editing a list of cities, and thus #{row} is essentially #{city}, it's strange that you reference the country via the state as in #{city.state.country} in the dropdown input value. Whilst that may work for displaying, that wouldn't work for editing/saving. Basically, you're here changing the country on a per-state basis instead of on a per-city basis. The currently selected state would get the new country instead of the currently iterated city. This change would get reflected in all cities of this state!
This is indeed not trivial if you'd like to continue with this data model. Ideally, you'd like to have a separate (virtual) Country property on City so that the changes doesn't affect the city's State property. You could make it just #Transient so that JPA doesn't consider it as a #Column as by default.
#Transient // This is already saved via City#state#country.
private Country country;
public Country getCountry() {
return (country == null && state != null) ? state.getCountry() : country;
}
public void setCountry(Country country) {
this.country = country;
if (country == null) {
state = null;
}
}
All in all, you should ultimately have this (irrelevant/default/obvious attributes omitted for brevity):
<p:dataTable value="#{someViewScopedBean.cities}" var="city">
...
<p:selectOneMenu id="country" value="#{city.country}">
<f:selectItems value="#{applicationBean.countries}" />
<p:ajax update="state" />
</p:selectOneMenu>
...
<p:selectOneMenu id="state" value="#{city.state}">
<f:selectItems value="#{applicationBean.getStates(city.country)}" />
</p:selectOneMenu>
...
</p:dataTable>
With an #{applicationBean} something like this:
#Named
#ApplicationScoped
public class ApplicationBean {
private List<Country> countries;
private Map<Country, List<State>> statesByCountry;
#EJB
private CountryService countryService;
#EJB
private StateService stateService;
#PostConstruct
public void init() {
countries = countryService.list();
statesByCountry = new HashMap<>();
}
public List<Country> getCountries() {
return countries;
}
public List<State> getStates(Country country) {
List<State> states = statesByCountry.get(country);
if (states == null) {
states = stateService.getByCountry(country);
statesByCountry.put(country, states);
}
return states;
}
}
(this is the lazy loading approach; you could also immediately fetch them all in #PostConstruct, just see what's better for you)
In this case, it is quite simple. There is no need to code any further. In the state menu, the following,
<f:selectItems var="state" value="#{cityManagedBean.selectedStates}"
itemLabel="#{state.stateName}" itemValue="#{state}"
itemLabelEscaped="true" rendered="true"/>
is just required to be modified as follows.
<f:selectItems var="state" value="#{row.state.country.stateTableSet}"
itemLabel="#{state.stateName}" itemValue="#{state}"
itemLabelEscaped="true" rendered="true"/>
Since, the entity object (row in this case) contains an embedded object of state which in turn, contains an object of country which finally contains a list of states corresponding to that country only as obvious.
So that
cityManagedBean.selectedStates
an extra managed bean method is now not required at all and needed to be modified as
row.state.country.stateTableSet
Where stateTableSet is a Set<StateTable> that contains a list of objects of the StateTable entity.
Also, listener in <p:ajax> is not required anymore. It should simply look like the following.
<p:ajax update="cmbStateMenu"/>
It is there just for the purpose of updating the state menu, when an item (country) is selected in the country menu.
The code in question should now look like the following.
<p:column id="country" headerText="Country" resizable="true" sortBy="#{row.state.country.countryName}" filterBy="#{row.state.country.countryName}" filterMatchMode="contains" filterMaxLength="45">
<p:cellEditor>
<f:facet name="output">
<h:outputLink value="Country.jsf">
<h:outputText value="#{row.state.country.countryName}"/>
<f:param name="id" value="#{row.state.country.countryId}"/>
</h:outputLink>
</f:facet>
<f:facet name="input">
<p:selectOneMenu id="cmbCountryMenu" converter="#{countryConverter}" value="#{row.state.country}" label="Country" required="true" filter="true" filterMatchMode="contains" effect="fold" rendered="true" editable="false" style="width:100%;">
<f:selectItems var="country" value="#{cityManagedBean.selectedCountries}" itemLabel="#{country.countryName}" itemValue="#{country}" itemLabelEscaped="true" rendered="true"/>
<p:ajax update="cmbStateMenu"/>
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
<p:column id="state" headerText="State" resizable="false" sortBy="#{row.state.stateName}" filterBy="#{row.state.stateName}" filterMatchMode="contains" filterMaxLength="45">
<p:cellEditor>
<f:facet name="output">
<h:outputLink value="State.jsf">
<h:outputText value="#{row.state.stateName}"/>
<f:param name="id" value="#{row.state.stateId}"/>
</h:outputLink>
</f:facet>
<f:facet name="input">
<p:selectOneMenu id="cmbStateMenu" converter="#{stateConverter}" value="#{row.state}" label="State" required="true" filter="true" filterMatchMode="contains" effect="fold" rendered="true" editable="false" style="width:100%;">
<f:selectItems var="state" value="#{row.state.country.stateTableSet}" itemLabel="#{state.stateName}" itemValue="#{state}" itemLabelEscaped="true" rendered="true"/>
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
Apology : I didn't mention in the question that I was retrieving a list of cities.
You need to fetch the state list based on the Country selected. You need a valueChangeListener for that.
Try this ( I've put both select one menu's in the same column for now)
In your xhtml
<p:column id="state" headerText="State" sortBy="#{row.state.stateName}" filterBy="#{row.state.stateName}">
<p:cellEditor>
<f:facet name="output">
<f:facet name="output">
<h:outputLink value="State.jsf">
<h:outputText value="#{row.state.stateName}"/>
<f:param name="id" value="#{row.state.stateId}"/>
</h:outputLink>
</f:facet>
<f:facet name="input">
<h:selectOneMenu id="cmbCountryMenu" style="width:100px" value="#{row.state.country}" converterMessage="Error message." label="Country" valueChangeListener = "#{countryController.handleCountrySelect}" immediate="true" converter="#{countryConverter}">
<f:selectItem itemLabel = "Select" itemValue="#{null}" />
<f:selectItems var="country" value="#{cityManagedBean.selectedCountries}" itemLabel="#{country.countryName}" itemValue="#{country}"/>
<p:ajax update="cmbStateMenu" />
</h:selectOneMenu>
<h:selectOneMenu style="width:100px" value="#{row.state}" valueChangeListener = "#{stateController.handleStateSelect}" immediate="false" id="cmbStateMenu" converter = "#{stateConverter}">
<f:selectItems var="state" value="#{row.state.country.stateTableSet}" itemLabel="#{state.stateName}" itemValue="#{state}" itemLabelEscaped="true" rendered="true"/>
<p:ajax update="#this" />
</h:selectOneMenu>
</f:facet>
</p:cellEditor>
in you controller
public void handleCountrySelect( ValueChangeEvent event )
{
setStates( ( ( Country) event.getNewValue() ) );
}

Resources