h:commandLink not working in ui:repeat - jsf

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.

Related

<h:inputText> in <ui:repeat> submits only the last row [duplicate]

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

How to supress JSF warning: Could not resolve NavigationCase for outcome: puh-repair.xhtml

I have a problem with the warning: Could not resolve NavigationCase for outcome:....
com.sun.faces.application.resource.ResourceHandlerImpl logMissingResource
WARNUNG: JSF1064: Resource /common-content.xhtml not resolvable
The warning does not seem to have any
affect to my application since i leverage the implicit navigation using JSF 2 with Mojarra 2.2.8 and Primefaces 3.5. I didn't define any navigation-case
within the faces-config.xml
The interesting thing about it, is that navigation itself works properly, but the warning is added to the Faces message queue and therefor shown within any message after a request.
The following code shows my home.xhtml which
<h:body class="page-container">
<p:growl id="message"></p:growl>
<div class="page-layout-container">
<div>
<ui:insert name="header">
<ui:include src="/template/puh-common-header.xhtml" />
</ui:insert>
</div>
<div>
<ui:insert name="menu">
<ui:include src="/template/puh-menu.xhtml" />
</ui:insert>
</div>
<div>
<h:panelGroup id="content" layout="block">
<ui:insert name="content">
<ui:include src="/main-content/#{mainContentController.getContent()}"/>
</ui:insert>
</h:panelGroup>
</div>
<div>
<ui:insert name="footer">
<ui:include src="/template/puh-common-footer.xhtml" />
</ui:insert>
</div>
</div>
</h:body>
The page just includes the appropriate compositions. I think here is the problem.
The outcome is always home.xhtml the pages referenced by the JSF warning are just compositions.
Edit:
The backing bean impementation of mainContentController:
#Named("mainContentController")
#SessionScoped
public class MainContentController implements Serializable {
/**
*
*/
private static final long serialVersionUID = -6818446964735212239L;
private Logger logger = LogManager.getLogger(MainContentController.class);
public static String PAGE_HOME = "puh-home.xhtml";
public static final String CONTENT_ERROR = "puh-error.xhtml";
public static String CONTENT_COMMON = "puh-common-content.xhtml";
public static String CONTENT_CONTACT = "puh-contact.xhtml";
public static String CONTENT_REPAIR = "puh-repair.xhtml";
public static String CONTENT_IMPRESSUM = "puh-impressum.xhtml";
private String content = CONTENT_COMMON;
private void showControllerState() {
logger.info("Page "+ content + ".");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Page "+ content + "."));
}
public String homeAction() {
content = CONTENT_COMMON;
showControllerState();
return content;
}
public String serviceAction() {
content = CONTENT_REPAIR;
showControllerState();
return content;
}
public String impressumAction() {
content = CONTENT_IMPRESSUM;
showControllerState();
return content;
}
public String contactAction() {
content = CONTENT_CONTACT;
FacesContext facesContext = FacesContext.getCurrentInstance();
String outcome = PAGE_HOME+"?faces-redirect=true"; // Do your thing?
// Because of Captcha Image
facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, null, outcome);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Redirect "+ content + "."));
showControllerState();
return content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
What does the managed bean function here return?:
<ui:insert name="content">
<ui:include src="/main-content/#{mainContentController.getContent()}"/>
</ui:insert>
because according to the warning:
WARNUNG: JSF1064: Resource /common-content.xhtml not resolvable
The file you are referencing does not seem to exist in the location which you have specified. It would also help if you posted the file structure of your project.
I solved the warning but ran into real problems.
I changed the following code snippet
<div>
<h:panelGroup id="content" layout="block">
<ui:insert name="content">
<ui:include src="/main-content/#{mainContentController.getContent()}"/>
</ui:insert>
</h:panelGroup>
</div>
I added the static "/main-content" path to the return value of the method "mainContentController.getContent()" which belongs to the dynamic Expression Language statement #{mainContentController.getContent().
The result is <div>
<h:panelGroup id="content" layout="block">
<ui:insert name="content">
<ui:include src="#{mainContentController.getContent()}"/>
</ui:insert>
</h:panelGroup>
</div>
and the backing bean looks like this
public class MainContentController implements Serializable {
private Logger logger = LogManager.getLogger(MainContentController.class);
public static String PAGE_HOME = "puh-home.xhtml";
public static String CONTENT_COMMON = "/main-content/puh-common- content.xhtml";
private String content = CONTENT_COMMON;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
private void handleNavigation() {
logger.info("Navigate to page "+ content + ".");
FacesContext facesContext = FacesContext.getCurrentInstance();
String outcome = PAGE_HOME+"?faces-redirect=true"; facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, null, outcome);
}
public String homeAction() {
content = CONTENT_COMMON;
handleNavigation();
return content;
}
}
I don't have a real answer for this kind of behaviour right now but i think
the problem is related to the life cycle of JSF.
To emphasize the issue.
There is differnence between
1. <ui:include src="/main-content/#{mainContentController.getContent()}"/>
and
2. <ui:include src="#{mainContentController.getContent()}"/>
Both statements (1 and 2) result into to exactly the same path but the behaviour of the container is different.

h:selectBooleanCheckBox action on select

I have a <h:selectBooleanCheckBox> as part part of my JSF which I want to run a bean method when it's state has changed from unchecked to checked.
I have the following controller bean
#Named
#ViewScoped
public class UserController {
#Inject
private UserService userService;
#Inject
private LocationService locationService;
private UserFilter userFilter;
private List<User> users;
private List<Location> locations;
#PostConstruct
public void init() {
users = userService.listAll();
locations = locationService.listAll();
userFilter = new UserFilter();
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public List<Location> getLocations() {
return locations;
}
public void setLocations(List<Location> locations) {
this.locations = locations;
}
public void listAllUsers() {
users = userService.listAll();
}
public void findUsers() {
// code that uses the UserFilter
// to decide which user filter find method to use
}
}
The UserFilter is a simple DTO
public class UserFilter {
private boolean allUsers = true;
private String username;
private String location;
//getters and setters
}
And my JSF has is like so
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Users</title>
</h:head>
<h:body>
<h1>Users</h1>
<h:form id="filterForm">
<h:selectBooleanCheckbox id="selectAll" value="#{userController.userFilter.allUsers}" title="allUsers">
<f:ajax render="filterGrid"/>
</h:selectBooleanCheckbox><h:outputText value ="All users"/>
<h:panelGrid id="filterGrid" columns="3">
<h:inputText id="userName" value="#{userController.userFilter.userName}" disabled="#{userController.userFilter.allUsers}"/>
<h:selectOneMenu id="selectLocation" value="#{userController.userFilter.location}" disabled="#{userController.userFilter.allUsers}">
<f:selectItems value="#{userController.locations}" var="location" itemValue="#{location.location}" itemLabel="#{location.location}"/>
</h:selectOneMenu>
<h:commandButton value="Filter" disabled="#{userController.userFilter.allUsers}" action="#{userController.findUsers()}"/>
</h:panelGrid>
</h:form>
<h:form rendered="#{not empty userController.users}">
<h:dataTable value="#{userController.users}" var="user">
<h:column>#{user.name}</h:column>
<h:column>#{user.location.location}</h:column>
<h:column><h:commandButton value="delete" action="#{userController.delete(user)}"/></h:column>
</h:dataTable>
</h:form>
<h:panelGroup rendered="#{empty userController.users}">
<p>Table is empty! Please add new items.</p>
</h:panelGroup>
<h3>Add user</h3>
<h:form id="user">
<p>Value: <h:inputText id="name" /></p>
<p>
<h:commandButton value="add" action="#{userController.add(param['user:name'])}"/>
</p>
</h:form>
</h:body>
</html>
As you can see by default it lists all users, then when the checkbox is unchecked you have the option to filter on username/location.
What I want is for the check box to run the userController.listAllUsers() method when it's state moves from unchecked to checked.
And a small additional question, how do I get the checkbox to appear in the same row as the panel grid items?
I have a habit of answering my own questions it seems! I needed an additional <f:ajax tag that rendered the user form and had the listener attribute set
So something like
<h:selectBooleanCheckbox id="selectAll" value="#{userController.userFilter.allUsers}" title="allUsers">
<f:ajax render="filterGrid"/>
<f:ajax render="usersForm" listener="#{userController.listAllUsers()}"/>
</h:selectBooleanCheckbox><h:outputText value ="All users"/>

Sending JSF textbox value to a managed bean function as a parameter [duplicate]

This question already has an answer here:
How to send form input values and invoke a method in JSF bean
(1 answer)
Closed 7 years ago.
I have two textboxes and one submit button in my login.xhtml page. I also have a bean. Here are the codes:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Welcome to Online Banking</title>
</h:head>
<h:body>
<h:outputText value="Online Banking System Login" ></h:outputText>
<h:form>
<h:panelGrid columns="2" border="1">
<h:outputText value="Username:"></h:outputText>
<h:inputText id="username" value="#{loginBean.username}"></h:inputText>
<h:outputText value="Password"></h:outputText>
<h:inputSecret id="password" value="#{loginBean.password}" > </h:inputSecret>
<h:commandButton value="Login" action="#{loginBean.loginCheck(username, password)}"></h:commandButton>
</h:panelGrid>
</h:form>
</h:body>
</html>
And the beans file:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package beans;
import javax.inject.Named;
import javax.enterprise.context.Dependent;
/**
*
* #author SUUSER
*/
#Named(value = "loginBean")
#Dependent
public class LoginBean {
/**
* Creates a new instance of LoginBean
*/
public LoginBean() {
}
private static String username="", password="";
public String getUsername(){
return username;
}
public String getPassword(){
return password;
}
public void setUsername(String Username){
username=Username;
}
public void setPassword(String Password){
password=Password;
}
public void loginCheck(String username, String password){
}
}
I will do the database check in my loginCheck function, so i need to pass the values of those two textboxes as a parameter. But i do not know how to do this. I just tried the code but it just passes empty strings as parameteres. Can anyone help me with this?
Thanks
Actually, you do not need to pass username and password parameters to a action method in your case.
A JSF page has a request life cycle. If you look inside JSF request life cycles, you will notice that values is applied to managed bean before the action.
Therefore, loginBean.username and loginBean.password values are set to managed bean username and password fields before the action. You can access them in the action method.
Your action will be
<h:commandButton value="Login" action="#{loginBean.loginCheck}"></h:commandButton>
and the action method
public String loginCheck(){
// search username and password in database.
// username, password field are set to values on page and accessible in the action method.
// Do not forget to navigate a proper page after according to login attempt.
}
For further reading
Communication in JSF 2.0 tutorial by BalusC
Basic Login Mechanism using Filters
JSF Request Life Cycle
How to pass value from textbox to Bean
1- create a .xhtml file
<h:form id="frm">
<h:inputText id="t1" value="#{user.x}" />
<h:commandButton action="#{user.check}" value="ok"/>
<h:outputText value="Value is #{user.msg}" ></h:outputText>
</h:form>
2- create a bean
ManagedBean(name="user")
RequestScoped
public class UserNumber {
int x;
String msg;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public void check()
{
if(x%2==0)
{
msg= "Even";
}
else
{
msg= "Odd";
}
}
public UserNumber() {
}
}
JSF automatically binds textbox values to variables in backing bean.
So you don't need to pass values to backing bean in calling function. You can just put like this
<h:commandButton value="Login" action="#{loginBean.loginCheck}"></h:commandButton>
And in bean you can use those as regular variable. I also suggest not to use static variables to store backing bean data.
public String loginCheck(){if(username == <fromDB> && password == <fromDB>) return "loginsuccess"; else return "loginfailure"; }
Hope this helps.
Well, from what I understand, do you that check in your loginCheck function the values of those textboxes. Automatically the JSF set the values of parameters to the variables, so you can work with them by gets(). For example is the following:
<h:form>
<h:panelGrid columns="2" border="1">
<h:outputText value="Username:"></h:outputText>
<h:inputText id="username" value="#{loginBean.username}"></h:inputText>
<h:outputText value="Password"></h:outputText>
<h:inputSecret id="password" value="#{loginBean.password}" > </h:inputSecret>
<h:commandButton value="Login" action="#{loginBean.loginCheck()}"></h:commandButton>
</h:panelGrid>
</h:form>
end in his ManagedBean is the following:
#ManagedBean(name= "loginBean")
public class LoginBean {
public LoginBean() {
}
// gets and sets
public void loginCheck(){
if(getUsername() != null && getPassword() != null){
// and here you chek database parameters
}
}
}

<h:inputText> doesn't seem to work within <ui:repeat>, only the last entry is submitted

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

Resources