The scenario: I want to partially execute and render part of a form in JSF, using AJAX, through the use of a commandLink. Since this will be done through AJAX, from my understanding ViewScoped should hold the values of the components in the page between AJAX requests.
I created an example below, where I have an input box where an Order Name is entered, the commandLink is pressed (labeled "Enter Order"), the order name from the inputText is added to an ArrayList named "orders", and the "orders" are displayed in a DataTable below the CommandLink.
The AJAX request from the CommandLink executes the inputBox that adds the String value to the "orderName" variable. The AJAX request sends an ActionEvent to the processDataTable() method, that takes the "orderName" value and adds it to the "orders" list. The "orderName" is then nulled. The AJAX request then renders the inputText component with the nulled "orderName", and the DataTable that displays all the previously entered orders, including the newly added "orderName" from this request.
Problem: The code works if I'm using a SessionScoped bean, but NOT if I use a ViewScoped bean. I added System.out.println()'s to the code to see what was happening. If I make the bean SessionScoped, then everything works as planned. The inputText value is set through the set method, the processOrder() method adds the "orderName" String to the "orders" List, and the DataTable is re-rendered to show this added name.
...
With a ViewScoped bean, the "orderName" value is set to the value of the InputText Component, but is "null" inside of the ProcessOrder() method, adds "null" to the "orders" List, and the DataTable has nothing to show besides null.
If I manually add an orderName in processOrder() with orders.add("Some Name") before the "orderName" is added, "orders" will then hold {"Some Order", "null"} and the change to the Orders is still NOT re-rendered with the AJAX.
All works great with a SessionScoped bean and NOT with a ViewScoped bean? I have also tried an Action attribute with the CommandLink instead of the ActionListener, and the same story.. works with SessionScoped but NOT with ViewScoped.
I can only think of two things here, 1) Either I am missing a crucial point in how AJAX and CommandLink work together, and how ViewScoped is supposed to work (very possible); or 2) for some reason, how I submit the AJAX call with CommandLink it is refreshing the page each time it is clicked, thus erasing previous values of the "orders" list. However this still does not explain why if I manually enter an order into the List, it is still not rendered in the AJAX request with a ViewScoped bean, but WILL with a SessionScoped bean.
Help please!!??
Here's my code:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;
#Named(value = "testBean")
#ViewScoped
public class DataTableTest implements Serializable {
private String orderName;
private List<String> orders = new ArrayList<String>();
public DataTableTest () {}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
System.out.println(orderName + " in setOrderName");
}
public List<String> getOrders() {
return orders;
}
public String processDataTable(ActionEvent event) {
System.out.println(orderName + " before getting added to orders list in processDataTable");
orders.add(orderName); // adds the orderName to the orders list, so it can be viewed in the DataTable
orderName = null; // nulling variable so it displays BLANK once again on the form
// Loops through and displays order names in the orders list
for (String name : orders) {
System.out.println(name + " in orders List");
}
return null;
}
}
and here's my jsf page:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="3">
<h:outputText value="Enter Order Name >>" />
<h:inputText id="orderName" value="#{testBean.orderName}" label="# {testBean.orderName}" />
<h:message for="orderName" />
</h:panelGrid>
<h:commandLink value="Enter Order" actionListener="#{testBean.processDataTable}">
<f:ajax execute="orderName dataTable" render="orderName dataTable" />
</h:commandLink>
<h:dataTable id="dataTable" value="#{testBean.orders}" var="order">
<f:facet name="header">Orders Entered Shown Below: </f:facet>
<h:column>
<h:outputText value="#{order}" />
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
You are using an CDI-Bean, but the #ViewScoped-Annotation, that you are using, is JSF-specific and does not work in a CDI-Bean.
You could use a JSF-managedBean instead
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean(name="testBean")
#ViewScoped
public class DataTableTest implements Serializable { .. }
If changing to a managedBean is not an option for you, take a look at CDI-specific #ConversationScope or Seam, where you have a #ViewScoped annotation that also works fine with CDI-Beans.
Related
EDIT 2: Why it is different
#BalusC, this is different from the post you say because I'm asking about an actionListener weird behavior that's not executing a method.
Also, In my test I'm following the Primefaces's documentation, applying fixed id attribute to every NamingContainer and checking the generated HTML (and has the expected id's)
The post you say it's a duplicate from this, was a good help but here we're talking about another problems.
I was working in a webapp's layout and found a problem updating the DOM. I'm working with Primefaces 5.2 and Glassfish 4.1. To isolate the problem I tried to test it in other project but I found another issue in my test. Maybe I'm missing something, so I need to ask in this forum.
I'll go backwards: I'll describe the test and then the real problem so you can see it has the same structure
THE FAILING TEST
index.xhtml
<h:body>
<h:form id="form-render">
<p:commandButton id="btnRender" value="Render" update=":form-rendered"
actionListener="#{renderView.setRender(true)}"/>
</h:form>
<h:form id="form-rendered">
<h:outputText value="Rendered in form-rendered" rendered="#{renderView.render}"/>
<p:panelGrid id="panel-grid" rendered="#{renderView.render}">
<p:outputLabel value="Rendered in form-rendered:panel-grid"/>
</p:panelGrid>
</h:form>
</h:body>
renderView.java
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class RenderView implements Serializable {
private static final long serialVersionUID = -1687524798440117276L;
private boolean render;
public RenderView() {
this.render = false;
System.out.println("Constructor - render=" + this.render);
}
public boolean isRender() {
return render;
}
public void setRender(boolean render) {
this.render = render;
System.out.println("Setter - render=" + this.render);
}
}
But when I run the test only renders the <h:outputText> component. I tried another options:
update=":form-rendered, :form-rendered:panel-grid": same result
update=":form-rendered:panel-grid": doesn't render anything
move rendered=#{...} attribute from <p:panelGrid> to <p:outputLabel> inside it: doesn't change
I don't know how to reference the <p:panelGrid> inside the <h:form>. According to Primefaces's documentation:
Update attribute takes a comma or white-space separated list of JSF component ids to be updated
So the first option should work, but it doesn't. What am I missing here?
THE REAL PROBLEM
The previous test came from a problem I was facing working with a layout. The layout's idea is the following:
In several pages I need to search something and show the results under the search form (with AJAX, of course).
Every page in the layout is a facelet template client (made with <ui:composition>)
This is ONLY the layout, there are not any queries, business logic or anything like that. The #ManagedBean classes I use are just creating dummy objects to populate the Primefaces's components
So I used the same structure of the previous test to solve the problem in every page. The funny thing is that I coded the first one and worked perfectly, but when I coded the second, fails on rendering the second form. Even more, I coded the third and worked perfectly, coded the fourth and fails!!!.
In my first solution attempt I used the following bean:
#ManagedBean // from javax.faces.bean
#ViewScoped // idem
public class SearchView {
private boolean showResults;
public SearchView() {
this.showResults = false;
System.out.println("Constructor-showResults=" + this.showResults);
}
public boolean isShowResults() {
return showResults;
}
public void setShowResults(boolean showResults) {
this.showResults = showResults;
System.out.println("Setter-showResults=" + this.showResults);
}
}
I'll copy-paste from a working example first and then form a failing example, so maybe you can see something I'm missing.
searchPage1.xhtml (working)
<ui:composition xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
template="./../templateMenu.xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<ui:define name="left">
<ui:include src="include/auMenu.xhtml"/>
</ui:define>
<ui:define name="content">
<h1>Search 1</h1>
<h:form id="form-busq-au">
<p:panelGrid columns="2" styleClass="ui-noborder">
<!--form fields -->
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults(true)}"
update=":form-res-au"/>
</p:panelGrid>
</h:form>
<h:form id="form-res-au">
<c:if test="#{searchView.showResults}">
<h1>Search Results</h1>
</c:if>
<p:panelGrid id="panelgrid-res-au" columns="2" rendered="#{searchView.showResults}">
<!-- other fields -->
</p:panelGrid>
</h:form>
</ui:define>
</ui:composition>
searchPage2.xhtml (not working)
<ui:composition xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
template="./../templateMenu.xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<ui:define name="left">
<ui:include src="include/menu.xhtml"/>
</ui:define>
<ui:define name="content">
<h:form id="form-busq-mt">
<h1>Search 2</h1>
<p:panelGrid columns="4" styleClass="ui-noborder">
<!-- form fields -->
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults(true)}"
update=":form-res-mt"/>
</p:panelGrid>
</h:form>
<h:form id="form-res-mt">
<c:if test="#{searchView.showResults}">
<h1>Search Results</h1>
</c:if>
<p:panelGrid columns="2" rendered="#{searchView.showResults}">
<!-- Other fields -->
</p:panelGrid>
</h:form>
</ui:define>
</ui:composition>
As you can see, it's the same structure in the test and the two pages, but the behavior is different. I don't know if this is a bug in primefaces or what.
Debugging the code I realized that in the second page, the setSearchResult(true) method is never called, don't know why. So I tried the following solution:
Failed solution attempt
Thinking that it was a problem with the SearchView Managed Bean, I made a change in the Controller: I made an abstract class with the attribute and methods and a #ManagedBean for each one of the web pages extending the abstract class. In code:
public abstract class SearchView {
private boolean showResults;
public SearchView() {
this.showResults = false;
System.out.println("Constructor-showResults=" + this.showResults);
System.out.println("Class: " + this.getClass().toString());
System.out.println("ID: " + this.getClass().hashCode());
}
public boolean isShowResults() {
return showResults;
}
public void setShowResults(boolean showResults) {
this.showResults = showResults;
System.out.println("Setter-showResults=" + this.showResults);
}
}
And for every search page:
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class ConcreteXXXSearchView extends SearchView implements Serializable {
private static final long serialVersionUID = -4717041016001640528L;
}
but the result is the same (setShowResult(true) is never called in the second page). The println() methods in the abstract class SearchView was to test that I'm instantiating different objects (indeed, there are different).
I tried the test's options but the problem is in the <p:commandButton> because actionListener is not working.
Summarizing
I'm facing two problems here:
In the test I need to render a <h:form> containing a UIComponent that implements NamingContainer(<p:panelGrid>)
In the project I need to know why the setShowResults(true) is not called in the failing page
Any help / guide is appreciated. Thanks in advance
EDIT: more info
In both TEST and REAL PROBLEM I checked the DOM with the browser inspector and the id attribute is correct for both <h:form id="form-res-mt"> and <p:panelGrid id="panelgrid-res-mt">
You have used actionListener="#{searchView.setShowResults()}" in your non-working approach.
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults()}"
update="form-res-mt"/>
But, searchView.setShowResults() method expects a boolean argument, change your code to:
<p:commandButton value="Search" icon="ui-icon-search" style="width: 100%;"
actionListener="#{searchView.setShowResults(true)}"
update="form-res-mt"/>
Also, use <ui:fragment rendered="yourCondition"> instead of <c:if test="condition"> because if component condition returns false ,it'll not attached to the component tree. And thus can't find the component in the UI Tree.
This question already has an answer here:
h:commandButton is not working once I wrap it in a <h:panelGroup rendered>
(1 answer)
Closed 6 years ago.
Here is a test case of my problem:
TestBean:
#ManagedBean
#ViewScoped
public class TestBean implements Serializable {
private static final long serialVersionUID = -2329929006490721388L;
private List<TestObject> testObjects;
private int selectedId;
public TestBean(){
List<TestObject> to = new ArrayList<TestObject>();
for(int i=0; i<10; i++){
TestObject o = new TestObject(i, "object-"+i);
to.add(o);
}
this.setTestObjects(to);
}
public void testAjaxListener(int id){
System.out.println("testAjaxListener("+id+")");
this.setSelectedId(id);
}
//+getters/setters
}
TestObject
public class TestObject {
private int id;
private String name;
public TestObject(int id, String name){
this.setId(id);
this.setName(name);
}
//+getters/setters
}
Test.xhtml:
<?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: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></h:head>
<h:body>
<h:form id="testForm">
<h:panelGroup rendered="#{param['view'] eq 'test'}">
<h2>DataTable</h2>
<h:dataTable var="o" value="#{testBean.testObjects}">
<h:column>
<h:commandLink value="#{o.name}" actionListener="#{testBean.testAjaxListener(o.id)}">
<f:ajax
render=":testForm:outputTest"
/>
</h:commandLink>
</h:column>
</h:dataTable>
<h2>output</h2>
<h:outputText id="outputTest" value="#{testBean.selectedId}" />
</h:panelGroup>
</h:form>
</h:body>
</html>
The problem is, that actionListener won't firing (i'm checking that with System.out.print as you can see). It works fine when i remove conditional render from panelGroup, so i think that is the issue - but how can i fix it?
I have readed those topics:
h:commandLink / h:commandButton is not being invoked,
f:ajax inside conditionally rendered custom tag - backing bean method not invoked
and many more, but it didn't solve my problem :(
Please help
It's because the #{param['view'] eq 'test'} didn't evaluate true while JSF is busy processing the ajax submit. It will then also consult the rendered, disabled and readonly attributes once again as safeguard against hacked requests. JSF namely doesn't include the request parameters in the <form action> URL as generated by <h:form>. This matches point 5 of commandButton/commandLink/ajax action/listener method not invoked or input value not updated.
There are several ways to get around this.
Set it as a property of the view scoped bean via <f:viewParam>.
<f:metadata>
<f:viewParam name="view" value="#{testBean.view}" />
</f:metadata>
...
<h:panelGroup rendered="#{testBean.view eq 'test'}">
Manually retain the param via <f:param> (you need to put it in every submit action!):
<h:commandLink ...>
<f:param name="view" value="#{param.view}" />
...
</h:commandLink>
Replace <h:form> by OmniFaces <o:form> which is capable of telling JSF that it must submit the form to an URL including the request parameters:
<o:form includeRequestParams="true">
...
</o:form>
See also:
Retaining GET request query string parameters on JSF form submit
For a huge project, I need to build multiple forms on a web page. Can't go into details, but assume to have the requested structure of a list of forms; using mojarra jsf 2.2.5
given Managed Bean:
#ManagedBean
#SessionScoped
public class Debug {
private final List<DebugBean> list;
public Debug() {
super();
this.list = new ArrayList<DebugBean>();
this.list.add(new DebugBean("label 1", "value 1"));
this.list.add(new DebugBean("label 2", "value 2"));
}
public String submit() {
LOGGER.info("list = " + this.list.toString());
return "";
}
public List<DebugBean> getList() {
return this.list;
}
}
The bean DebugBean is simple, just contains two variables label and value, both String, and its setter and getter.
Now the xhtml page:
<h:body style="height: 100%">
<ui:repeat value="#{debug.list}" var="x">
<h:form>
<h:panelGrid columns="2">
<h:outputText value="#{x.label}" />
<h:inputTextarea value="#{x.value}" />
</h:panelGrid>
<h:commandButton action="#{debug.submit}" value="ok" />
</h:form>
</ui:repeat>
</h:body>
The problem is in changing the first value. When I change the second one, the logger gives me the new value as expected. But changing the first one, the logger gives me the old list, as if I would have reloaded the complete form without any changes. What's the problem here, and more important: what can I do to make it work?
The solution is simple, but worse. I don't know why, but ui:repeat does not behave as h:dataTable does. So replacing ui:repeat by h:dataTable solves the problem, but means page layout rework (which is a bigger problem, but can be done by me).
I am working on JSF2.0 project where I need to get value in backing bean from previous screen I have explained it below
my first screen
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
>
<h:body>
<h:form>
<h:outputText value="LocationID" />
<h:inputText value="#{getDetails.locationID}"/>
<h:commandButton value="Add OS" action="#{getDetails.addOSDetails}"/>
</h:form>
</h:body>
</html>
My backing bean when the commandButton is invoked
#ManagedBean(name="getDetails")
#RequestScoped
public class GetDetailsBean {
private String locationID;
public String getLocationID() {
return locationID;
}
public void setLocationID(String locationID) {
this.locationID = locationID;
}
public String addOSDetails(){
return "/app_pages/addOS";
}
}
My second screen which is addOS is
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
>
<h:body>
<h:form>
<h:outputText value="Enter Value" />
<h:inputText value="#{addOS.addValue}"/>
<h:commandButton value="Add OS" action="#{addOS.save}"/>
</h:form>
</h:body>
</html>
I want the LocationID entered in the first screen to be available in this backing bean
#ManagedBean(name="addOS")
#RequestScoped
public class AddOS {
private String addValue;
public String getAddValue() {
return addValue;
}
public void setAddValue(String addValue) {
this.addValue = addValue;
}
public String save(){
return "app_pages/success";
}
}
I donot want values to be set in session.Can be used.
Thoughts and help please
Thanks.
If you really intend to do a forward, as you do in your question, the submitted on the first page data is already there when the second page is rendered: remember, the forward is done within the same HTTP request. So, what you ultimately need is to keep the value for a subsequesnt POST request. You can do it, for example, by storing the information is a hidden field on the second page:
<h:inputHidden value="#{getDetails.locationID}" />
And that's basically it.
If you intend to do a redirect, thus by appending ?faces-redirect=true to a navigation case outcome, you need to store that information in EL flash to be able to retrieve it in the recepient page: there are two requests to be done. So, change your first bean's action method to the following:
public String action() {
...
FacesContext.getCurrentInstance().getExternalContext().getFlash().put("locationID", locationID);
return "result?faces-redirect=true";
}
This way on the recepient page it will be available in flash map, thus by
#{flash.locationID}
in EL scope and
(String)FacesContext.getCurrentInstance().getExternalContext()
.getFlash().get("locationID");
in Java code.
I have a custom tagfile with a form:
<h:form>
<h:commandButton value="click">
<f:ajax event="click" listener="#{bean[method]}" />
</h:commandButton>
</h:form>
I'm conditionally rendering it by ajax as below:
<h:panelGroup id="test">
<h:form>
<h:commandButton value="click">
<f:ajax event="click" listener="#{backingTest.updateFlag}" render=":test"/>
</h:commandButton>
</h:form>
<h:panelGroup rendered="#{backingTest.flag}">
<my:customtag bean="#{backingTest}" method="printMessage"/>
</h:panelGroup>
</h:panelGroup>
This is the associated backing bean:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class BackingTest {
private boolean flag = false;
public void printMessage() {
System.out.println("hello");
}
public void updateFlag() {
flag = true;
}
public boolean getFlag() {
return flag;
}
}
When I click the first command button, then the updateFlag() method is properly invoked and the second command button is properly shown. But when I then click the second command button, it never hits the printMessage() method. In the web browser's JS console and HTTP traffic monitor I can see that the click event is successfully fired and that the XHR POST request is successfully being sent.
If I remove the rendered attribute, then everything works as expected.
How is this caused and how can I solve it? I'm using Mojarra 2.1.25.
Your concrete problem is caused by 2 facts:
When JSF needs to decode a form submit action or a submitted input value, then it also checks if the component is rendered or not (as safeguard against hacked/tampered requests).
Request scoped beans are recreated on every HTTP request (an ajax request counts also as one request!).
In your specific case, the rendered condition has evaluated false while JSF needs to decode the form submit action and therefore the non-rendered input/command components are never processed.
Putting the bean in view scope should fix it. Below example assumes JSF 2.x.
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
And below example assumes JSF 2.2+ with CDI:
import javax.inject.Named;
import javax.faces.view.ViewScoped;
#Named
#ViewScoped
Or, if the techncial requirement is to keep the bean in request scope, then carry around the condition behind rendered attribute in a <o:inputHidden>. Adjust your snippet to include it in the <h:form>.
<o:inputHidden value="#{backingTest.flag}" />
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated - point 6
How to choose the right bean scope?