I have an <ui:repeat> with <ui:inputText>:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
template="./templates/masterLayout.xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<ui:define name="content">
<ui:repeat value="#{genproducts.dbList()}" var="itemsBuying">
<div class="indproduct">
<p class="center">#{itemsBuying.name}</p>
<div class="center">
<h:form style="margin-left: auto; margin-right: auto;">
<h:inputText value="#{itemsBuying.amount}" />
<h:commandLink action="#{shoppingCart.addToCart(itemsBuying)}" value="add" />
</h:form>
</div>
</div>
</ui:repeat>
</ui:define>
</ui:composition>
This is the #{genproducts} backing bean:
#ManagedBean(name = "genproducts")
#ViewScoped
public class Genproducts{
public List<Product> dbList() throws SQLException {
List<Product> list = new ArrayList<>();
...
return list;
}
}
This is the Product entity:
#ManagedBean(name = "product")
#RequestScoped
public class Product {
private int amount;
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
}
In my case, there are four products from dbList() method. For the first three products, when I input a different value, the default value appears in action method. Only for the last product, it works as expected.
How is this caused and how can I solve it?
It's caused because you're (re)creating the list in the getter method behind <ui:repeat value>. This method is invoked during every iteration round. So, every next iteration will basically trash the values set during the previous iteration. In the action method, you end up with the list as created during the last iteration round. That's why the last entry seems to work fine.
This approach is indeed absolutely not right. You should not be performing business logic in getter methods at all. Make the list a property and fill it only once during bean's (post)construction.
#ManagedBean(name = "genproducts")
#ViewScoped
public class Genproducts{
private List<Product> list;
#PostConstruct
public void init() throws SQLException {
list = new ArrayList<>();
// ...
}
public List<Product> getList() {
return list;
}
}
Which is to be referenced as
<ui:repeat value="#{genproducts.list}" var="itemsBuying">
See also
How and when should I load the model from database for h:dataTable
Why JSF calls getters multiple times
Related
My application has a query param in the url which is used to render different data using the same xhtml (all dynamic content) in a datatable.
Scope of the bean is session scope, the datatable renders all data, and has lots of input elements which open different dialogs.
If I open another browser tab sending a different query param, the datatable renders perfectly, however if I go back to the first tab, all commandlinks won't invoke the actions and the whole application will start to act very erratically.
According to my research this is because I changed the data in the datatable, I tried naming it with dynamic ids, dynamic widget names, but nothing seems to work.
<f:metadata>
<f:viewParam name="param" value="#{moduleBean.param}"/>
<f:viewAction action="#{moduleBean.setup}" />
</f:metadata>
<c:set var="module" value="#{moduleBean.param}" />
<p:dataTable id="#{module}-dataTable" value="#{moduleBean.model[module]}" var="data">
<p:commandLink action="#{moduleBean.openModuleDetails}" update=":#{module}-searchDialog" oncomplete="PF('#{module}-searchWidget').show();">
<f:param name="module" value="#{module}" />
<f:param name="dataRow" value="#{data.dbKey}" />
</p:commandLink>
</p:dataTable>
#Named
#javax.faces.view.ViewScoped
public class ModuleBean implements Serializable {
private String param;
public void setup() throws IOException {
this.model.put(this.param, new LazyDataModel(this.param));
}
public Map<String, LazyDataModel> getModel() {
return model;
}
}
This builds all the expected html with all correct ids in each tab, however JSF is still not processing the action inside the commandlink. Needless to say, if I stick to only one browser tab everything works perfectly.
Sometimes it starts working after clicking twice in the link, but going back and forth between the browser tabs will eventually always crash it.
Adding an action listener to the commandlink didnt fix it either.
Any suggestions on how to make JSF treat the same datatable as different entities on the same page but with different parameters ?
Without knowing more about the underlying bean - if you place your moduleBean in #SessionScoped this would be the expected behavior. The session (and session scoped beans) are shared between browser tabs. So you cannot rely on the underlying values from two different tabs.
Try changing to #RequestScoped/#ViewScoped for the backing values of the table data.
Here is a complete solution that works, note that this uses PrimeFaces 6.2, Apache Commons and Lombok;
#Data
#Named
#ViewScoped
public class TableTestBackingBean implements Serializable {
private int param;
#Inject
private PersonsBean personsBean;
public void onClicked() {
System.out.println("Clicked fine!");
}
public List<Person> getPersons() {
return personsBean.getPersons()[param];
}
}
#Data
#ApplicationScoped
public class PersonsBean {
#Data
#AllArgsConstructor
public class Person {
private String firstName;
private String lastName;
private int age;
}
private List<Person> persons[];
#PostConstruct void init() {
persons = new List[4];
for (int j = 0; j < 4; j++) {
persons[j] = new ArrayList<>();
for (int i = 0; i < 30; i++) {
final String firstName =
RandomStringUtils.randomAlphanumeric(10);
final String lastName =
RandomStringUtils.randomAlphanumeric(10);
final int age = RandomUtils.nextInt(0, 120);
persons[j].add(new Person(firstName, lastName, age));
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui" xmlns:f="http://java.sun.com/jsf/core">
<f:metadata>
<f:viewParam name="param" value="#{tableTestBackingBean.param}"/>
</f:metadata>
<h:head>
<title>Test</title>
</h:head>
<h:body>
<p:dataTable value="#{tableTestBackingBean.persons}" var="t">
<p:column headerText="First Name"><h:outputText value="#{t.firstName}" /></p:column>
<p:column headerText="Last Name"><h:outputText value="#{t.lastName}" /></p:column>
<p:column headerText="Age"><h:outputText value="#{t.age}" /></p:column>
</p:dataTable>
<h:form>
<p:commandButton action="#{tableTestBackingBean.onClicked}" value="Click Me!" />
</h:form>
</h:body>
</html>
This uses an application scoped bean for the table data, keeping it completely static. This works without a hitch and the table renders the data differently based on the parameter passed in param.
I am trying to update the page size of a PrimeFaces datatable dynamically, after the datatable is displayed on the page but I can't seem to be able to do that. I am using a lazy loaded datatable if that matters... putting an EL expression in the rows attribute and doing a table refresh does not work and causes the paginator to return no data.
Any ideas if this is a bug and how to fix it?
Thanks
You need not refresh or update the datatable. Use EL expression in rowsPerPageTemplate attribute of <p:dataTable> and after loading datatable just change the rows number.
<p:dataTable value="#{managedBean.users}" var="user" lazy="true" paginator="true" rows="10" rowsPerPageTemplate="10,25 #{fn:length(managedBean.users)}">
And include this xmlns:fn="http://java.sun.com/jsp/jstl/functions" in page.
What exactly are you trying to do? This works for me (Mojarra 2.1.26, PrimeFaces 3.5):
#ManagedBean
#ViewScoped
public class Bean {
public class Item {
private String name;
public Item(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
private List<Item> items;
public Bean() {
Item item1 = new Item("item1");
Item item2 = new Item("item2");
items = Arrays.asList(item1, item2);
}
public List<Item> getItems() {
return items;
}
private int tableSize = 1;
public int getTableSize() {
return tableSize;
}
public void actionSwitchSize() {
tableSize = tableSize == 1 ? 2 : 1;
}
}
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head />
<h:body>
<h:form>
<p:dataTable id="table" var="item" value="#{bean.items}"
rows="#{bean.tableSize}" paginator="true">
<p:column>
#{item.name}
</p:column>
</p:dataTable>
<p:commandButton value="switch size" action="#{bean.actionSwitchSize}"
update="table" />
</h:form>
</h:body>
</html>
I have an <ui:repeat> with <ui:inputText>:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
template="./templates/masterLayout.xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<ui:define name="content">
<ui:repeat value="#{genproducts.dbList()}" var="itemsBuying">
<div class="indproduct">
<p class="center">#{itemsBuying.name}</p>
<div class="center">
<h:form style="margin-left: auto; margin-right: auto;">
<h:inputText value="#{itemsBuying.amount}" />
<h:commandLink action="#{shoppingCart.addToCart(itemsBuying)}" value="add" />
</h:form>
</div>
</div>
</ui:repeat>
</ui:define>
</ui:composition>
This is the #{genproducts} backing bean:
#ManagedBean(name = "genproducts")
#ViewScoped
public class Genproducts{
public List<Product> dbList() throws SQLException {
List<Product> list = new ArrayList<>();
...
return list;
}
}
This is the Product entity:
#ManagedBean(name = "product")
#RequestScoped
public class Product {
private int amount;
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
}
In my case, there are four products from dbList() method. For the first three products, when I input a different value, the default value appears in action method. Only for the last product, it works as expected.
How is this caused and how can I solve it?
It's caused because you're (re)creating the list in the getter method behind <ui:repeat value>. This method is invoked during every iteration round. So, every next iteration will basically trash the values set during the previous iteration. In the action method, you end up with the list as created during the last iteration round. That's why the last entry seems to work fine.
This approach is indeed absolutely not right. You should not be performing business logic in getter methods at all. Make the list a property and fill it only once during bean's (post)construction.
#ManagedBean(name = "genproducts")
#ViewScoped
public class Genproducts{
private List<Product> list;
#PostConstruct
public void init() throws SQLException {
list = new ArrayList<>();
// ...
}
public List<Product> getList() {
return list;
}
}
Which is to be referenced as
<ui:repeat value="#{genproducts.list}" var="itemsBuying">
See also
How and when should I load the model from database for h:dataTable
Why JSF calls getters multiple times
I want to display details about a product when a user clicks on a link with the name of that product.
When debugging my code I see that the details method shown in the code below doesn't run.
My code:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:body>
<ui:composition template="/Shared/layout.xhtml" >
<ui:define name="title">Product</ui:define>
<ui:define name="body">
<div id="contents" >
<h3> List Product in Site </h3>
<ul>
<ui:repeat value="#{productBean.listProduct}" var="p">
<div id="list-item">
<div class='item' >
<div class='item-img' >
<h:form>
<input type="hidden" name="productId" value="#{p.productId}" />
<h:commandLink action="#{productBean.details}">
<h:graphicImage url="#{p.picture}" alt='No Picture' />
</h:commandLink>
</h:form>
</div>
<h:form>
<input type="hidden" name="productId" value="#{p.productId}" />
<h:commandLink value="#{p.name}" action="#{productBean}" >
</h:commandLink>
</h:form>
</div>
</div>
</ui:repeat>
</ul>
<h:form>
<h:commandLink value="text" action="#{productBean.test}"> <!-- test -->
</h:commandLink>
</h:form>
</div>
</ui:define>
</ui:composition>
</h:body>
ProductBean:
#ManagedBean
#RequestScoped
public class ProductBean implements Serializable {
private List<Products> listProduct;
private List<Categories> listCategory;
private Products product;
public Products getProduct() {
return product;
}
public void setProduct(Products product) {
this.product = product;
}
public ProductBean() {
listCategory = new ProductsDB().listCategory();
}
private int categoryId;
public int getCategoryId() {
return categoryId;
}
public void setCategoryId(int categoryId) {
this.categoryId = categoryId;
}
public String listProductByCt() {
try {
String value = FacesContext.getCurrentInstance().
getExternalContext().getRequestParameterMap().get("categoryId");
setCategoryId(Integer.parseInt(value));
if (categoryId == 0) {
return "index";
}
listProduct = new ProductsDB().listProducts(categoryId);
return "product";
} catch (Exception e) {
return "error";
}
}
public String details() {
String s = "";
try {
String productId = FacesContext.getCurrentInstance().
getExternalContext().getRequestParameterMap().get("productId");
if (productId != null) {
product = new ProductsDB().getProductById(productId);
return "details";
}
} catch (Exception e) {
return "error";
}
return "error";
}
public String test()
{
return "s";
}
public List<Categories> getListCategory() {
return listCategory;
}
public void setListCategory(List<Categories> listCategory) {
this.listCategory = listCategory;
}
public List<Products> getListProduct() {
return listProduct;
}
public void setListProduct(List<Products> listProduct) {
this.listProduct = listProduct;
}
}
I've also tried with another method called test. This too is referenced from an action on products.xhtml, but that action isn't placed inside a <ui:repeat>. In this case, the method does gets executed.
The test method:
public String test() {
String productId = "IP16G";
product = new ProductsDB().getProductById(productId);
return "details";
}
Which version of JSF are you using?
The code you've shown with different forms and hidden inputs inside a ui:repeat is not really idiomatic JSF. There's also a typo in the action of your first command link. It says produtBean but I guess it should be productBean. JSF will give you an exception though if you click on that command link.
Did you got that exception, or did nothing happen?
In case nothing happened, a likely cause is that the data you used to render your command links (#{productBean.listProduct}) is not available anymore after the post back. What method are you using to obtain this data and what is the scope of your bean? In many cases you should be using #ViewScoped and initialize the data when the request is not a post back.
Also, make sure there is no parent component of the <ui:repeat> that has a rendered attribute that is set to false by default. In case this attribute will be set to true at some point of the life-cycle, this may well be -after- the click on the link is processed. If that happens, it will still be false when the click is being processed, which has the effect that it will silently be ignored.
You can test the last effect by putting your test command link right next to the <ui:repeat>:
<ui:repeat value="#{productBean.products}" var="product">
...
</ui:repeat>
<h:commandLink value="test" action="#{productBean.test}" />
Although your approach with the multiple forms and hidden fields does work, a more idiomatic JSF version would be:
<h:form>
<ui:repeat value="#{productBean.products}" var="product">
<h:commandLink action="#{productBean.details(product)}">
<h:graphicImage url="#{product.picture}" alt="No Picture" />
</h:commandLink>
<h:commandLink value="#{product.name}" action="#{productBean.details(product)}" />
</ui:repeat>
</h:form>
With your bean's details method as:
public String details(Product product) {
this.product = product;
return "details";
}
(getting more off-topic, but if all your method does is returning a navigation case, you might want to consider using a direct link like <h:link> with the Id of your product as a parameter. This will use GET to go to your destination page, which in this case is much cleaner)
EDIT
In order to test whether the problem isn't somewhere else, try the following code. It's a single page and a single backing bean. Add these to your project and request the page. This should work.
forminloop.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:body>
<ui:repeat value="#{formInLoopBacking.items}" var="item">
<h:form>
<h:commandLink value="#{item}" action="#{formInLoopBacking.action}"/>
</h:form>
</ui:repeat>
</h:body>
</html>
FormInLoopBacking.java
import javax.faces.bean.ManagedBean;
#ManagedBean
public class FormInLoopBacking {
private String[] items = {"A", "B"};
public String[] getItems() {
return items;
}
public void action() {
System.out.println("Action called");
}
}
By looking at your above code
you have open <ui:repeat> tag but not closed it and try to put whole <ui:repeat> inside a form and check on your page is a <h:form> tag inside a h:form. If its not work then check your bean is in view scope or not if not put in view scope.
Original question is below, but as I have come up with a more minimal example to demonstrate this problem, and figured it should go at the top.
Anyway, it appears that ui:repeat tags are processed before checking to see if parent elements are actually rendered. To recreate this, here is the facelet (minimalTest.xhtml):
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Test JSF <ui:repeat> inside <h:panelGroup rendered="false"></title>
</h:head>
<h:body>
<h:form>
<h1>Testing</h1>
<h:panelGroup rendered="false">
<span>#{minimalTestBean.alsoThrowsException}</span>
<ul>
<ui:repeat value="#{minimalTestBean.throwsException}" var="item">
<li>#{item}</li>
</ui:repeat>
</ul>
</h:panelGroup>
</h:form>
</h:body>
</html>
With using this bean (MinimalTestBean.java):
package com.lucastheisen.beans;
import java.io.Serializable;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class MinimalTestBean implements Serializable {
private static final long serialVersionUID = 9045030165653014015L;
public String getAlsoThrowsException() {
throw new RuntimeException( "rendered is false so this shouldnt get called either" );
}
public List<String> getThrowsException() {
throw new RuntimeException( "rendered is false so this shouldnt get called" );
}
}
From this example you can see that the h:panelGroup that contains the ui:repeat is statically set to rendered=false which I would assume would mean that none of the EL expressions inside of that h:panelGroup would get executed. The EL expressions just call getters which throw a RuntimeException. However, the ui:repeat is actually calling the getter for its list thus causing the exception even though it should not be getting rendered in the first place. If you comment out the ui:repeat element, no exceptions get thrown (even though the other EL expression remains in the h:panelGroup) as I would expect.
Reading other questions here on stackoverflow leads me to believe that is likely related to the oft-referred-to chicken/egg issue, but I am not sure exactly why, nor what to do about it. I imagine setting the PARTIAL_STATE_SAVING to false might help, but would like to avoid the memory implications.
---- ORIGINAL QUESTION ----
Basically, I have a page that conditionally renders sections using <h:panelGroup rendered="#{modeXXX}"> wrapped around <ui:include src="pageXXX.xhtml" /> (per this answer). The problem is that if one of the pageXXX.xhtml has a <ui:repeat> inside of it, it seems to get processed even when the containing <h:panelGroup> has rendered=false. This is a problem because some of my sections rely on having been initialized by other sections that should be visited before them. Why is the included pageXXX.xhtml getting processed?
This is a painful bug and incredibly hard to boil down to a small example, but here is the most minimal case I could build that demonstrates the issue. First a base page:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Test JSF <ui:include></title>
</h:head>
<h:body>
<h:form>
<h1>#{testBean.title}</h1>
<h:panelGroup rendered="#{testBean.modeOne}">
<ui:include src="modeOne.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{testBean.modeTwo}">
<ui:include src="modeTwo.xhtml" />
</h:panelGroup>
</h:form>
</h:body>
</html>
As you can see this page will conditionally include either the modeOne page or the modeTwo page based upon the value in the testBean bean. Then you have modeOne (the default):
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">
<ui:composition>
<span>Okay, I'm ready. Take me to </span>
<h:commandLink action="#{testBean.setModeTwo}">mode two.</h:commandLink>
</ui:composition>
</html>
Which in my real world app would be a page that sets up things needed by modeTwo. Once set up, an action on this page will direct you to modeTwo:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">
<ui:composition>
<div>Here is your list:</div>
<ui:repeat value="#{testBeanToo.list}" var="item">
<div>#{item}</div>
</ui:repeat>
</ui:composition>
</html>
The modeTwo page basically presents a details for the modeOne page in a ui:repeat as the actual information is in a collection. The main managed bean (TestBean):
package test.lucastheisen.beans;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class TestBean implements Serializable {
private static final long serialVersionUID = 6542086191355916513L;
private Mode mode;
#ManagedProperty( value="#{testBeanToo}" )
private TestBeanToo testBeanToo;
public TestBean() {
System.out.println( "constructing TestBean" );
setModeOne();
}
public String getTitle() {
System.out.println( "\ttb.getTitle()" );
return mode.getTitle();
}
public boolean isModeOne() {
return mode == Mode.One;
}
public boolean isModeTwo() {
return mode == Mode.Two;
}
public void setModeOne() {
this.mode = Mode.One;
}
public void setModeTwo() {
testBeanToo.getReadyCauseHereICome();
this.mode = Mode.Two;
}
public void setTestBeanToo( TestBeanToo testBeanToo ) {
this.testBeanToo = testBeanToo;
}
private enum Mode {
One("Mode One"),
Two("Mode Two");
private String title;
private Mode( String title ) {
this.title = title;
}
public String getTitle() {
return title;
}
}
}
Is the bean for all the main data, and the TestBeanToo bean would be for the details:
package test.lucastheisen.beans;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class TestBeanToo implements Serializable {
private static final long serialVersionUID = 6542086191355916513L;
private ObjectWithList objectWithList = null;
public TestBeanToo() {
System.out.println( "constructing TestBeanToo" );
}
public String getTitle() {
System.out.println( "\ttb2.getTitle()" );
return "Test Too";
}
public List<String> getList() {
System.out.println( "\ttb2.getList()" );
return objectWithList.getList();
}
public void getReadyCauseHereICome() {
System.out.println( "\ttb2.getList()" );
objectWithList = new ObjectWithList();
}
public class ObjectWithList {
private List<String> list;
public ObjectWithList() {
list = new ArrayList<String>();
list.add( "List item 1" );
list.add( "List item 2" );
}
public List<String> getList() {
return list;
}
}
}
<ui:repeat> does not check the rendered attribute of itself (it has actually none) and its parents when the view is to be rendered. Consider using Tomahawk's <t:dataList> instead.