I have a datatable with lazy load and livescroll that works fine at first glance.
In the beginning the datatable loads items 0-20, when I scroll down items 20-40 are loaded and added to the datatable so that 0-40 are shown.
Problem: When I update the complete datatable (or form) via ajax the datatable is still 40 rows large but the items from 0-20 are repeated in rows 21-40.
The parameters of the LazyDataModel load() method are strange:
first: 0
pageSize: 20
items.xhtml
<p:dataTable var="_item" value="#{controller.lazyModel}"
selectionMode="single" selection="#{controller.selection}" rowKey="#{_item.id}"
scrollable="true" scrollRows="20" scrollHeight="125"
liveScroll="true" lazy="true" >
<p:commandButton value="Update" update="#form"
action="#{controller.updateSomething}" />
ItemController.java
#ViewScoped
#ManagedBean(name = "controller")
public class ItemController
{
private LazyDataModel<Item> itemLazyModel;
private Item selectedItem;
#EJB(lookup = "ejbpath...")
protected DBService dbService;
#PostConstruct
public void init()
{
itemLazyModel = new ItemLazyModel(dbService);
}
public void updateSomething()
{
// do something unrelated to the item list
}
// Getter and Setter for itemLazyModel and selectedItem
}
ItemLazyModel.java
public class ItemLazyModel extends LazyDataModel<Item> {
private DBService dbService;
private Map<String, Item> data;
public ItemLazyModel(DBService service)
{
this.dbService = dbService;
data = new LinkedHashMap<>();
setRowCount(dbService.getItemsSize());
}
#Override
public Item getRowData(String rowKey)
{
return data.get(rowKey);
}
#Override
public Object getRowKey(Item object)
{
return object.getId();
}
#Override
public List<Item> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters)
{
List<Item> result = dbService.getItems(first, pageSize);
for (Item item : result)
{
data.put(item.getId(), item);
}
return result;
}}
When changing to a paginated lazy datatable everything is fine. The datatable is on the correct 2nd page after the update and shows items 21-40.
If I set the pageSize manually and use getPageSize() in the load method, I am able to return the correct items from 0-40. But the rendered datatable still has the same problem with repeating items 1-20.
Is there maybe an incompatiblity between lazy, liveScroll and ajax updates where the datatable forgets its current scroll position (like the page when paginated)?
JSF: 2.2.13 (Mojarra)
Primefaces: 6.1
Related
I use a managed bean with a view scoped because I need it ( for a p:datatable with lazy and selection modes ), but i'd like to do some things into this managed bean at the render response phase, is there a convenient way to do that ?
I need a view scoped to save a lazyDataModel, but I noticed that the rowCount method is called several times, each time a request ( Select count ) is executed.
So I deciced to save the result of the request. But if I add a data and refresh the datatable with ajax, the row count still contain the same result because of the scope. I use a boolean to know if the rowcount method has been already executed and I can set this boolean to false each time I add or remove a data but if I could do that a the render response phase it would be more convenient for me.
#ManagedBean
#ViewScoped
public class ListBean {
protected MyLazyDataModel myLazyDataModel = new MyLazyDataModel();
public MyLazyDataModel getMyLazyDataModel() {
return myLazyDataModel;
}
public void reloadList() {
this.reloadList = true;
}
protected class MyLazyDataModel extends LazyDataModel{
private int rowCount ;
#Override
public List load(int first, int pageSize, String sortField,
SortOrder sortOrder, Map<String, Object> filters) {
...
}
#Override
public Object getRowKey(Type object) {
...
}
#Override
public Type getRowData(String rowKey) {
...
}
#Override
public int getRowCount() {
if( reloadList ) {
this.rowCount = getDao().getRowCount().intValue();
reloadList = false;
}
return rowCount;
}
}
}
Thanks.
I am learning JavaEE and I am playing around with ajax call with jsf and managedbean. I am trying to display the text as soon as i change the value from the drop down list. I see many people have the same problem on stackoverflow and i try to follow answers that are marked as accepted but I still can't make it work. Can someone please tell me where my error is?
Here is the jsf code:
<h:form>
<h:selectOneMenu id="productlist" value="#{productRepoMB.selectedProduct}">
<f:selectItems value="#{productRepoMB.productList}" var="product" itemValue="#{product.productID}" />
<f:ajax event="valueChange"
render="result"
listener="#{productRepoMB.selectMenuListener}" />
</h:selectOneMenu>
text changed: <h:outputText id="result" value="#{productRepoMB.text}" />
</h:form>
Here is managed bean code, Products list is populated from a database:
#Named
#RequestScoped
public class ProductRepoMB implements Serializable {
#EJB
private ProductsRepo productRepo;
private Products selectedProduct;
private List<Products> productList;
private String text="init text"; //use for testing ajax call
public ProductRepoMB() {
}
public Products getSelectedProduct() {
return selectedProduct;
}
public List<Products> getProductList() {
productList = productRepo.findAll();
return productList;
}
//The following code are used to testing ajax only!
public void selectMenuListener(AjaxBehaviorEvent e) {
setText("changed!");
}
public String getText() {
return text;
}
public void setText(String text){
this.text = text;
}
}
My question is how to get value from selection in 'selectOneMenu' component. I use POJO not String type. I try to display the name property of selected object in inputText. I use commandButton to refresh value in inputText as in code below. But the problem is that nothing appears in inputText. I'm not sure there is need to use converter but I tried and it also hasn't worked.
here is my .jsp file:
<p:selectOneMenu value="#{appointentBean.selectedSpecialization}">
<f:selectItems value="#{appointentBean.specializationResult}" var="i" itemValue="#{i}" itemLabel="#{i.name}"/>
</p:selectOneMenu>
<p:commandButton value="Szukaj" >
<p:ajax update="textid" />
</p:commandButton>
<p:inputText id="textid" value="#{appointentBean.selectedSpecialization.name}" />
appointmentBean:
#ManagedBean
#ViewScoped
#SessionScoped
public class appointentBean
{
private ArrayList<Specialization> specializationResult;
private Specialization selectedSpecialization;
public ArrayList<Specialization> getSpecializationResult()
{
//Here retrievie objects list from database and it works
return specializationResult;
}
public void setSpecializationResult(ArrayList<Specialization> result) {
this.specializationResult = result;
}
public Specialization getSelectedSpecialization() {
return selectedSpecialization;
}
public void setSelectedSpecialization(Specialization selectedSpecialization) {
this.selectedSpecialization = selectedSpecialization;
}
}
Specialization.java:
#Entity
#Table(name="Specializations")
public class Specialization
{
#Id
#GeneratedValue
private int specialization_id;
#Column(name="name")
private String name;
public int getSpecialization_id() {
return specialization_id;
}
public void setSpecialization_id(int specialization_id) {
this.specialization_id = specialization_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
What is more. If I do not make selection on the list NullPointerExcetion appears. But when I make choice i doesn't. So the object is set after selection.
Give a name to your Managed Bean like this
1. #ManagedBean(name ="appointentBean")
2. It should be in Session Scoped or View Scoped not in Both
Your code works perfectly on my End. I did changes to
ArrayList<Specialization> getSpecializationResult() like this:
public ArrayList<Specialization> getSpecializationResult()
{
//Here retrievie objects list from database and it works
specializationResult = new ArrayList<Specialization>();
Specialization specialize= new Specialization();
specialize.setName("Vinayak");
specialize.setSpecialization_id(1);
specializationResult.add(specialize);
return specializationResult;
}
It worked . So, make the necessary changes and let us know.
EDIT 2
Whenever we Deal with POJO's at that time we have to deal with Converter.
Why Custom Converter is the question is what you want to ask now. Refer Custom Converter
These are the steps to create Custom Converter
1. Create a converter class by implementing javax.faces.convert.Converter interface.
2. Override both getAsObject() and getAsString() methods.
3. Assign an unique converter ID with #FacesConverter annotation present in javax.annotation.
First of all I have created a POJOConverter class for your Specialization class
package primefaces1;
import java.util.ArrayList;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
#FacesConverter(forClass=Specialization.class)
public class PojoConverter implements Converter{
public static List<Specialization> specilizationObject;
static {
specilizationObject = new ArrayList<Specialization>();
specilizationObject.add(new Specialization("Vinayak", 10));
specilizationObject.add(new Specialization("Pingale", 9));
}
public Object getAsObject(FacesContext facesContext, UIComponent
component, String submittedValue) {
if (submittedValue.trim().equals("")) {
return null;
} else {
try {
for (Specialization p : specilizationObject) {
if (p.getName().equals(submittedValue)) {
return p;
}
}
} catch(NumberFormatException exception) {
throw new ConverterException(new
FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion
Error", "Not a valid Specialization"));
}
}
return null;
}
public String getAsString(FacesContext facesContext, UIComponent
component, Object value) {
if (value == null || value.equals("")) {
return "";
} else {
return String.valueOf(((Specialization) value).getName());
}
}
}
Following changes has been made to your managed Bean class. To overcome the NUll Pointer Exception
package primefaces1;
import java.util.ArrayList;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean(name = "appointentBean")
#SessionScoped
public class appointentBean {
private ArrayList<Specialization> specializationResult;
private Specialization selectedSpecialization ;
#PostConstruct
public void init() {
selectedSpecialization = new Specialization();
selectedSpecialization.setName(new String());
selectedSpecialization.setSpecialization_id(0);
}
public appointentBean() {
specializationResult= (ArrayList<Specialization>)
PojoConverter.specilizationObject;
}
public ArrayList<Specialization> getSpecializationResult() {
// Here retrievie objects list from database
//and it works
return specializationResult;
}
public void setSpecializationResult(ArrayList<Specialization> result) {
this.specializationResult = result;
}
public Specialization getSelectedSpecialization() {
if (this.selectedSpecialization != null)
System.out.println("getSelectedSpecialization----"
+ this.selectedSpecialization.getName());
return this.selectedSpecialization;
}
public void setSelectedSpecialization(Specialization
selectedSpecialization) {
this.selectedSpecialization = selectedSpecialization;
}
}
I have made some minute changes to your xhtml for showing values.
<h:body>
<h:form id="me">
<p:selectOneMenu value="#{appointentBean.selectedSpecialization}" >
<f:selectItem itemLabel="Select One" itemValue=""></f:selectItem>
<f:selectItems value="#{appointentBean.specializationResult}"
var="result" itemValue="#{result}" itemLabel="#{result.name}" />
</p:selectOneMenu>
<p:commandButton value="Szukaj" update="me:textid">
</p:commandButton>
<h:outputText value="NAME: "></h:outputText>
<h:outputText id="textid" value="#{appointentBean.selectedSpecialization.name}" rendered="#{not empty appointentBean.selectedSpecialization}"/>
</h:form>
</h:body>
I find myself in the same situation that user2374573, SelectOneMenu, was populated correctly using a custom converter, but the selected item was null. The proposed solution is a variation of the custom converter, but it doesn't solve the problem (at least for me). The value selecting does not arrive as explained in the Primefaces documentation, this occurs because SelectOneMenu operates with String and not with Pojos. After studying In the end I have opted for an intermediate solution.
Instead of having a variable of type pojo to store the value, I use just having a String that stores the id of the element as follows.
This solution has been useful for the SelectOneMenu and also for loading the Targer in the DualList used in the Primefaces Picklist. It is not an ideal solution, but it saves the problem.
Java View
public class PickListView implements Serializable {
private static final long serialVersionUID = 1L;
private List<CviConcesione> listaConcesion;
private CviConcesione concesionSeleccionada;
private String concesionSeleccionadaS;
#Autowired
private ConcesionesBO concesionesBO;
#PostConstruct
public void init() {
}
public List<CviConcesione> getListaConcesion() {
if (null != listaConcesion && !listaConcesion.isEmpty()) {
return listaConcesion;
} else {
listaConcesion = new ArrayList<CviConcesione>();
listaConcesion = concesionesBO.consultaTodasConcesiones();
return listaConcesion;
}
}
public void setListaConcesion(List<CviConcesione> listaConcesion) {
this.listaConcesion = listaConcesion;
}
public ConcesionesBO getConcesionesBO() {
return concesionesBO;
}
public void setConcesionesBO(ConcesionesBO concesionesBO) {
this.concesionesBO = concesionesBO;
}
public CviConcesione getConcesionSeleccionada() {
return concesionSeleccionada;
}
public void setConcesionSeleccionada(CviConcesione concesionSeleccionada) {
this.concesionSeleccionada = concesionSeleccionada;
}
public String getConcesionSeleccionadaS() {
return concesionSeleccionadaS;
}
public void setConcesionSeleccionadaS(String concesionSeleccionadaS) {
this.concesionSeleccionadaS = concesionSeleccionadaS;
}
}
Html Code for select one menu
<p:selectOneMenu
id="concesionR"
value="#{pickListView.concesionSeleccionadaS}"
style="width:125px"
dynamic="true"
converter="#{concesionConverter}">
<f:selectItem itemLabel="Seleccione" itemValue="" />
<f:selectItems value="#{pickListView.listaConcesion}"
var="concesion"
itemLabel="#{concesion.conCodigo} - #{concesion.conDescripcion}"
itemValue="#{concesion.conCodigo}"
ajax = "true"
/>
<p:ajax update="lineaR" process="#form" />
</p:selectOneMenu>
a
Class converter
#FacesConverter("concesionConverter")
public class ConcesionesConverter implements Converter {
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if(value != null && value.trim().length() > 0) {
try {
PickListView service = (PickListView) fc.getExternalContext().getApplicationMap().get("pickListView");
return service.getListaConcesion().get(Integer.parseInt(value));
} catch(NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid theme."));
}
}
else {
return null;
}
}
public String getAsString(FacesContext fc, UIComponent uic, Object object) {
if(object != null) {
return String.valueOf(((CviConcesione) object).getConId());
}
else {
return null;
}
}
}
This solution does not manage to bring the pojo, but lets you know that it has been selected, showing pojo values.
i have the object of this structure
public class Cleanse {
private Contacts contact;
private List<Contacts> dnb360;
private String operation;
public String getOperation() {
return operation;
}
public void setOperation(String operation) {
this.operation = operation;
}
public Contacts getContact() {
return contact;
}
public void setContact(Contacts contact) {
this.contact = contact;
}
public List<Contacts> getDnb360() {
return dnb360;
}
public void setDnb360(List<Contacts> dnb360) {
this.dnb360 = dnb360;
}
In jsp i am getting the list of cleanse objects
can anyone tell me how to bind this list and get the submitted value in contoller
Binding in the jsp is not that difficult. If you're using core jstl tags (which you should), you only have to iterate over the lists entries and do with them what ever you want:
<c:forEach items="${dnb360}" var="s">
<b>${s}</b>
</c:forEach>
Since you're talking about 'submitting', I guess you#re trying to set up a form here, which makes it even easier (use spring's form tag here) <%# taglib uri="http://www.springframework.org/tags/form" prefix="form" %>:
<form:checkboxes items="${dnb360}" path="dnb360" />
For retrieving such a bound value and convert back to your object, I'd suggest using the #InitBinder annotation. You annotate a method with that and define how to bind a String value back to your object when retrieving this String through a method which has #ModelAttribute("dnb360") dnb360,...
#InitBinder
public void initBinder(WebDataBinder binder) {
//custom editor to bind Institution by name
binder.registerCustomEditor(Contacts.class, new PropertyEditorSupport() {
#Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(xxx); //most likely obtained through database call
}
});
}
If you need more help, please feel free to ask.
I'm using JSF 2.0 and EJB 3.1 in the Glassfish v3 app server. And I'm facing actually the follwing problem:
In a MenagedBean with RequestScope I want to access a session object (an EJB with #Stateful) which should store some session relevant information as the seleced category, the seleced page (with a datatable paginator for each category), etc. - nothing special I think.
The first time a category is selected, the datatable is created and displayed. Allright so far.
Now, if an item (row) is clicked (to show the item's details) or if the next page should be displayed, the session (the stateful EJB) is recreated and again the default values are used to display and render the page.
The code looks like:
#ManagedBean
#RequestScoped
public class TableViewBean {
#EJB
SessionBean session;
public DataModel getTable() {
return session.getDataModel();
}
public SessionBean getSession(){
return session;
}
public void next() {
session.getPaginator().nextPage();
session.resetList();
}
public void previous() {
session.getPaginator().previousPage();
session.resetList();
}
// some other code
}
and the session EJB:
#Stateful
public class SessionBean {
private String selectedType = "Entity";
private DataModel dataModel;
private int rowsPerPage = 5;
private Paginator paginator;
public void setSelectedType(String type){
if(!type.equalsIgnoreCase(selectedType)){
selectedType = type;
updateService();
}
resetList();
}
public void resetList() {
dataModel = null;
}
public void resetPagination() {
paginator = null;
}
public int getRowsPerPage() {
return rowsPerPage;
}
public void setRowsPerPage(int rowsPerPage) {
this.rowsPerPage = rowsPerPage;
resetList();
resetPagination();
}
public Paginator getPaginator() {
if (paginator == null) {
paginator = new Paginator(rowsPerPage) {
#Override
public int getItemsCount() {
return selectedService.getCount();
}
#Override
public DataModel createPageDataModel() {
DataModel model = null;
if(selectedService != null){
model = new ListDataModel(....);
}
return model;
}
};
}
return paginator;
}
public DataModel getDataModel() {
if(dataModel == null){
dataModel = getPaginator().createPageDataModel();
}
return dataModel;
}
}
If I change the Scope of the ManagedBean to SessionScope everything works fine, but I don't like this, because of use of memory concerns.
What's wrong with my code...please help me.
Greetz, Gerry
Your RequestScoped ManagedBean is re-instantiated for each request (that's what RequestScoped means after all). Therefore, with each instantiation it gets injected with a new SFSB instance.