Form submit in conditionally rendered component is not processed - ajax

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?

Related

JSF 2 <f:ajax> with page navigation

My requirement is to trigger and ajax request upon the button click and show validation errors with out page refresh. Also if there is no error, navigate to second view. Below is the code im trying. Im using jsf 2.1.7 with Jboss 7.1.1 final.
<h:form>
<h:inputText value="#{helloBean.name}"></h:inputText>
<h:commandButton value="Welcome Me" action="#{helloBean.goToWelcome}">
<f:ajax event="click" listener="#{helloBean.goToWelcome}"></f:ajax>
</h:commandButton>
</h:form>
HelloBean.java
#ManagedBean
#SessionScoped
public class HelloBean implements Serializable {
public String goToWelcome(){
System.out.println("in goToWelcome");
return "welcome";
}
}
I have a welcome.xhtml in the same folder as above xhtml and i can see the goToWelcome() method also being fired but the navigation does not happen. i assume its because as per the spec listener attribute should have a method with void return type and the returned string from goToWelcome() is ignored. So is there any way to achieve my requirement. Any kind of help would be highly appreciated. Thanks.
Basically, you need to return a navigation case outcome from an action method to do navigation. Note that AJAX listener is incapable to do navigation, at least directly. If you don't want to perform navigation, you could just return null from your action method. Also, navigation won't happen if there are conversion/validation errors, as well as your action method won't be called. For this reason you need to assign a bunch of <h:message>, or a global <h:messages> to display the error messages.
To combine it, the following suffices to achieve your functionality.
The view:
<h:form>
<h:messages id="messages">
<h:inputText value="#{helloBean.name}" />
<h:commandButton value="Do AJAX validation and navigate if necessary" action="#{helloBean.goToWelcome}">
<f:ajax execute="#form" render="messages" />
</h:commandButton>
</h:form>
The bean:
#ManagedBean
#ViewScoped
public class HelloBean implements Serializable {
public String goToWelcome(){
//do business job
if(/* some condition met */) {
return null;
} else {
return "nextpage";
}
}
}
Related reading on your topic
JSF f:ajax listener vs commandButton action;
How to use both Navigation Rule and f:ajax;
Communication in JSF 2.0, section "Ajax validation".
You probably need to redirect the page (assuming you don't have validation errors, it should work)
return "nextpage.jsf?faces-redirect=true";

nested jsf listener not called in #ViewScope

Currently I have a problem similar to
jsf listener not called inside nested ui:repeat
After I got the solution of this problem I upgraded Mojarra to Version 2.1.16.
Everthing works fine, up to the moment I was implementing the below-mentioned view.
The Code is simplified to the basics.
xhtml:
<form>
<div>
// listener is called
<h:commandLink value="test1">
<f:ajax event="click" listener="#{testBean.testListener}" />
</h:commandLink>
<ui:repeat var="testList" value="#{testBean.testList}">
// listener is not called !
<h:commandLink value="test2">
<f:ajax event="click" listener="#{testBean.testListener}" />
</h:commandLink>
</ui:repeat>
</div>
</form>
bean:
#ManagedBean(name="testBean")
#ViewScoped
public class TestBean {
private Collection<TestObj> testList;
public Collection<TestObj> getTestList() {
return this.testList;
}
// the listener
public void testListener() {
...
}
}
Even if I change ui:repeat to c:forEach, the problem is the same!
Just if I use #SessionScope instead of #ViewScope the 2nd commandLink (inside the iteration) calls the listener. But I'd like to avoid #SessionScope at this bean.
Do you have any hints for me about the reason of this problem and how to handle it?

Not able to validate input text field rendered using ajax in jsf 2.0

<h:form>
<h:selectManyMenu id="carsList"
value="#{bean.carList}"
style="width:400px; height:100px" label="List">
<f:selectItems value="#{bean.cars}" var="i"
itemLabel="#{i.code}" itemValue="#{i.Name}" />
<f:selectItem itemLabel="Other" itemValue="other"/>
<f:ajax event="change" execute="#this" render="othermodel"/>
</h:selectManyMenu>
<br></br>
<h:panelGroup id="othermodel">
<h:outputText value="Others:" style="margin-top:10px;color:red;font-weight:bold;"
rendered="#{bean.carList.contains('other')}" />
<h:inputText size="50" id="otherinput" required="true"
rendered="#{bean.carList.contains('other')}"/>
<h:message for="otherinput"></h:message>
</h:panelGroup>
<h:commandButton value="Next" action="#{bean.submitForm}"/>
</h:form>
My bean is requestScoped and when my carList has a value other i am able to show the panelGrid but when user don't enter any value in the input field rendered using AJAX , even i specificed required=true but it's not getting validated. And value of the input text box is null in the backend code.
How can I do the validation for the input field rendered using AJAX and why my value is coming as null? I am using JSF 2.0
The rendered attribute is re-evaluated during the request of the form submit. Your bean is request scoped and thus recreated on every single request (also ajax requests) with all properties set to default. So the carList property will also be emptied and the rendered attribute would evalute false before the update model values phase has filled the carList. So, for JSF, the <h:inputText> is never rendered and thus it won't be processed.
Put the bean in the view scope instead. It'll live as long as you interact with the same view by (ajax) postbacks. It's the right scope for dynamic ajax based forms with a lot of conditional rendering.
See also:
How to choose the right bean scope?
I tried this with #ViewScope. When I made my Spring managed service as transient I was getting null reference after deserialization. So, i tried the below method to get the Spring Service.
#ManagedBean(name="bean")
#ViewScoped
public class Bean implements Serializable {
#ManagedProperty(value="#{appService}")
private transient AppService appService;
// Getting AppService Object which is singleton in the application during deserialization
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
FacesContext context = FacesContext.getCurrentInstance();
appService = (AppService)context.getApplication()
.evaluateExpressionGet(context, "#{appService}", AppService.class);
stream.close(); // not sure i have to do this
}
}
This is working for me without any issues.

JSF: only able to use sessionscoped view with ajax commandlink

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.

JSF ajax commandbutton from multiple forms within same page reinitiliazes the viewscoped bean

changeThe page has 2 forms. In the first form, the ajax-button initializes an attribute of the viewscoped managed bean. In the second form, the ajax-button uses that attribute to do some stuff. Problem is that the method called by the second form button never gets invoked. Instead, the viewscoped bean is re-initialized and when I hit the same button again, of course the attribute is null resulting in NPE.When I put the 2 buttons within the same form, then everything works as expected.Is this normal: should ajax enabled buttons always reside within the same form?
<h:form id="frmDetail">
<h:commandButton id="btn_changePlant" title="#{msg.ttChangePlant}" immediate="true" image="/resources/img/edit.png">
<f:ajax event="click" render=":fsDetailPlant :headerMsg" listener="{assortiment.detailPlantId}"/>
</h:commandButton>
</h:form>
...some other code ...
<h:form id="frmOffers">
<h:commandButton id="btn_offers" title="#{msg.ttOfferPlant}" immediate="true" value="#{msg.btn_offers}">
<f:ajax event="click" render=":fsDetailPlant :headerMsg" listener="{assortiment.changeOffers}"/>
</h:commandButton>
</h:form>
and the managed bean looks like
#ManagedBean
#ViewScoped
public class Assortiment extends BeanObject implements Serializable {
....
public void detailPlantId(AjaxBehaviorEvent actionEvent) {
clear();
plantdetail = facade.findPlantdetail(plantdetailsIdModel.getRowData());
}
public void changeOffers(AjaxBehaviorEvent actionEvent) {
System.out.println("........ Assortiment.changeOffers(AjaxBehaviorEvent actionEvent)");
if (containerItems.isEmpty()) {
SessieUtils.fillPropertyItems(containerItems, PlantProperty.codeValuesList(ECodeType.logistic, "L02"), facade.getLocale());
}
//
if (offersModel == null) {
offersModel = new ListDataModel<OffersDto>(plantdetail.getOffersList());
}
}
It might just be that the listener attributes need the # prefix,
listener="{assortiment.changeOffers}"
should be
listener="#{assortiment.changeOffers}"

Resources