I am novice for JSF and I have a problem with my very simple Facelets view:
<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core" >
<h:head>
<title></title>
</h:head>
<h:body>
<h:form id="form">
<h:panelGrid id="ciccio" columns="2">
<h:outputText value="Nome " />
<h:inputText value="#{thinBean.nome}" />
<h:commandButton id="ok" value="OK" />
<h:commandButton id="vai" value="Go" rendered="#{not empty thinBean.nome}" action="Vista1" />
</h:panelGrid>
</h:form>
</h:body>
</html>
and with my simple Backing Bean.
package magazzino;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class ThinBean implements Serializable {
private String nome;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
}
After first call, only first button appears.
When I entered test on the field nome and submit this form by clicking button identified by ok, also second button appears.
When I click on second button, identified by vai, nothing happen: Vista1 is not rendered.
I don't understand this behavior: why Invoke Application phase is skipped?
Thanks
The rendered attribute of an UICommand component is also evaluated during apply request values phase, as part of safeguard against tampered/hacked requests wherein hackers try to invoke actions of UICommand components which are not rendered for non-admin users, for example.
In your particular case, you're using a request scoped bean. Thus, the bean is destroyed by end of every request and recreated in beginning of every request. Submitting the form by the first button counts as one request. Submitting the form by the second button counts as another request.
The UIInput values are set as managed bean property during update model values phase, which is after the apply request values phase. Thus, when the second button is pressed, the request scoped bean is newly created with all properties set to default (null). During apply request values phase, the rendered attribute is checking the input value, but it isn't been set yet and the bean is new and empty. So it won't consider the button as rendered and skip the decoding of the action event.
If you put the bean in the view scope, then it'll work for the very simple reason that the view scoped bean instance will live as long as you're interacting with the same view by returning null or void.
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
An alternative is checking the raw HTTP request parameter in the rendered attribute instead of the model value.
<h:inputText binding="#{nome}" value="#{thinBean.nome}" />
<h:commandButton id="ok" value="OK" />
<h:commandButton id="vai" value="Go" rendered="#{not empty param[nome.clientId]}" action="Vista1" />
See also:
How to choose the right bean scope?
Related
I have a JSF page that loads the properties of an object (for which the id is passed in the URL). The loading can last more seconds, so I would like to display a wait/busy indicator or a "Loading..." message.
This is done using "viewAction"
<f:metadata>
<f:viewAction action="#{myBean.loadParams}" />
</f:metadata>
Is there a simple way to accomplish this goal? I'm using Primefaces.
PrimeFaces has already a component ready for that: the <p:outputPanel deferred="true">. You only need to make sure that the #{heavyBean} is only referenced in a component (and thus definitely not in a tagfile like <c:xxx> for the reasons explained here) within the <p:outputPanel> and not somewhere else.
...
#{notHeavyBean.property}
...
<p:outputPanel deferred="true">
...
#{heavyBean.property}
...
</p:outputPanel>
...
#{anotherNotHeavyBean.property}
...
Then you can do the heavy job in its #PostConstruct method. Do the job you originally did in <f:viewAction> there in the #PostConstruct.
#Named
#ViewScoped
public class HeavyBean implements Serializable {
#PostConstruct
public void init() {
// Heavy job here.
}
// ...
}
If you need to access properties of other beans, simply #Inject those beans in the HeavyBean. E.g. in case you needed the ID view param:
<f:viewParam name="id" value="#{notHeavyBean.id}" />
#Inject
private NotHeavyBean notHeavyBean; // Also #ViewScoped.
#PostConstruct
public void init() {
Long id = notHeavyBean.getId();
// Heavy job here.
}
The <p:outputPanel> already comes with an animated gif. You can easily customize it via CSS.
.ui-outputpanel-loading {
background-image: url("another.gif");
}
I would like to propose also this simple approach:
one "landing" page (the page where we first navigate in) with a wait indicator and an autoRun remoteCommand with an event that read the parameter "param" from the URL and save it in the bean.
the remoteCommand does a redirect to another page (where the long-running method loadParams is executed)
In this way the wait indicator is shown until the second page is ready to be displayed.
Do you see any weaknesses?
Here the landing page:
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
...
</h:head>
<f:metadata>
<f:event type="postAddToView" listener="#{notHeavyBean.readProperty}" />
<f:viewParam name="param"/>
</f:metadata>
<h:body>
<p:outputPanel layout="block">
<i class="fa fa-circle-o-notch fa-spin layout-ajax-loader-icon" aria-hidden="true" style="font-size: 40px;position: relative;top: 50%;left: 50%;"></i>
</p:outputPanel>
<h:form>
<p:remoteCommand action="#{notHeavyBean.redirect}" autoRun="true"/>
</h:form>
</h:body>
I am seeing different behaviors of and in a page containing multiple forms.
Here is my backing bean:
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class MultiFormBean
{
String inputText1 = "";
String inputText2 = "";
#PostConstruct
public void initializeBean(){
System.out.println("PostConstruct Called ------------------");
}
public String getInputText1()
{
return inputText1;
}
public void setInputText1(String inputText1)
{
this.inputText1 = inputText1;
}
public String getInputText2()
{
return inputText2;
}
public void setInputText2(String inputText2)
{
this.inputText2 = inputText2;
}
public void doSubmit1() {
inputText2 = inputText1;
}
public void doSubmit2() {
inputText1 = inputText2;
}
}
When i use the following xhtml , clicking Submit1 and Submit2 any number of times won't call #PostConstruct more than once:
<h:body>
<h:form id="firstForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget1"/>
<h:inputText id="first_input" value="#{multiFormBean.inputText1}"/>
<h:commandButton id="click1" action="#{multiFormBean.doSubmit1}" value="submit1" type="submit"
onclick="javascript:jsf.ajax.request(this, event, {execute:'firstForm', render:'renderTarget1 secondForm'}); return false;">
</h:commandButton>
</h:form>
<h:form id="secondForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget2"/>
<h:inputText id="second_input" value="#{multiFormBean.inputText2}"/>
<h:commandButton id="click2" action="#{multiFormBean.doSubmit2}" value="submit2" type="submit"
onclick="javascript:jsf.ajax.request(this, event, {execute:'secondForm', render:'renderTarget2 firstForm'}); return false;">
</h:commandButton>
</h:form>
</h:body>
But the following xhtml would call #PostConstruct more than once:
<h:body>
<h:form id="firstForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget1"/>
<h:inputText id="first_input" value="#{multiFormBean.inputText1}"/>
<a4j:commandButton id="click1" action="#{multiFormBean.doSubmit1}" value="submit1" type="submit" execute="#form" render="renderTarget1,secondForm"/>
</h:form>
<h:form id="secondForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget2"/>
<h:inputText id="second_input" value="#{multiFormBean.inputText2}"/>
<a4j:commandButton id="click2" action="#{multiFormBean.doSubmit2}" value="submit2" type="submit" execute="#form" render="renderTarget2,firstForm"/>
</h:form>
</h:body>
Please can anyone help me use the <a4j:commandButton> instead of <h:commandButton>
Also i see that i cannot call the method doSubmit2() with a4j commandButton
I think that problem here is in bug inside JSF2 and Richfaces4. From 4 version Richfaces started using JSF embedded ajax capabilities. And There is a bug with using multiple forms on page with ajax requests. The problem there that richfaces renders special hidden input with the id of currently rendered view state. This id is changed when new view is rendered. And it is also submitted with every request to show that it belongs to some specific view. So when you have multiple forms on the same page after first ajax request the view state is getting the wrong place and it can be not submitted again second time. Sometimes behavior looks like very very wierd with no logical description.
PostConstruct is called twice because server thinks that two requests belong to different views(view state is not sumbitted) and as far as bean is view scoped it is created twice. After clicking aroung ajax can completelly stop working with this because server woukd not recognize the view(probably what you see when you can not click second submit button).
In the first place I recommend you to use latest available version of JSF and Richfaces. This bug (and many more) may be already fixed there.
I have the following 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">
<head>
<title>TODO supply a title</title>
</head>
<body>
<f:metadata>
<f:viewParam id="productCV" name="productName" value="#{productBean.product}"
converter="#{productConverter}" required="true"/>
</f:metadata>
<ui:composition template="/templates/mastertemplate.xhtml">
<!-- Define the page title for this page-->
<ui:define name="pageTitle">
<h:outputFormat value="#{msgs.productPageTitle}">
<f:param value="#{productBean.product.description}"/>
</h:outputFormat>
</ui:define>
<!-- Pass the categoryName parameter to the sidebar so the category of this product is highlighted-->
<ui:param name="categoryName" value="#{productBean.product.categoryName}"/>
<ui:define name="content">
<!-- If productconversion failed, show this error-->
<h:message id="error" for="productCV" style="color: #0081c2;" rendered="#{productBean.product == null}" />
<!-- If productconversion succeeded show the product page-->
<h:panelGroup rendered="#{productBean.product != null}">
<p>#{productBean.product.description} #{productBean.product.categoryName}</p>
<h:form>
<h:commandLink action="#{cartBean.addItemToCart(productBean.product)}">
<f:ajax event="action" render=":cart :cartPrice" />
<h:graphicImage value="resources/img/addToCart.gif"/>
</h:commandLink>
</h:form>
</h:panelGroup>
</ui:define>
</ui:composition>
</body>
</html>
At the top I accept a String as GET param which I run through a converter and then get a Product object, I place this in the productBean.product, that bean has a setter and getter for the Product attribute, that's all.
I then use this object to show info etc. this works fine. I also add commandLink to add it to my cart using AJAX. This refuses to work if my ProductBean is in RequestScope, when I put it in SessionScope it works, but will only add the product 1 time.
As best I know this should be a straight forward RequestScope, I don't understand why it does work with SessionScope.
I have read through this post but I don't think I'm violating any of those rules.
For completeness, this is my ProductBean:
import be.kdg.shop.model.stock.Product;
import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
#Named
#RequestScoped
public class ProductBean {
private static final Logger logger = Logger.getLogger(ProductBean.class.getName());
private Product product;
public ProductBean() {}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
Your bean is request scoped. So the bean instance lives as long as a single HTTP request-response cycle.
When the page with the form is requested for the first time, a new bean instance is created which receives a concrete product property as view parameter. After generating and sending the associated response, the bean instance is garbaged, because it's the end of the request.
When the form is submitted, effectively a new HTTP request is fired and thus a new bean instance is created with all properties set to default, including the product property. This way #{productBean.product} is null for the entire request. The rendered attribute of a parent component of the command link will evaluate false. The command link action is therefore never decoded. This matches point 5 of commandButton/commandLink/ajax action/listener method not invoked or input value not updated which you already found, but apparently didn't really understood.
The solution is to put the bean in the view scope. A view scoped bean lives as long as you're interacting (submitting/postbacking) with the same JSF view. Standard JSF offers #ViewScoped for this. As you're using CDI instead of JSF to manage beans, your best bet is the CDI #ConversationScoped. This is relatively clumsy (you've to start and end the scope yourself), so some CDI extension such as MyFaces CODI which offers a #ViewAccessScoped may be more useful.
See also:
How to choose the right bean scope?
I'm experiencing strange behaviour with my session scoped bean. I used following imports and annotations to make it sessionscoped:
EDIT : more Code
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
#Named
#SessionScoped
public class DetailsBean implements Serializable {
private LinkedHashMap<String, String> folder;
#Inject
private ApplicationBean appBean;
#Inject
private UserBean userBean;
#PostConstruct
public void resolveID() {
this.folder = new LinkedHashMap<String, String>();
for (LinkedHashMap<String, String> tempfolder : appBean.getRepositoryContent()) {
if (tempfolder.get("text:nodeid").equals(URLid)) {
this.folder = tempfolder;
}
}
}
Code Snippet of JSF page :
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:rich="http://richfaces.org/rich">
<f:metadata>
<f:viewParam name="id" value="#{detailsBean.URLid}" required="true" requiredMessage="You must provide an Object Id"/>
<f:event type="preRenderView" listener="#{detailsBean.resolveID}" />
</f:metadata>
<h:head>
<title>Dataset #{detailsBean.name}</title>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="2" columnClasses="fixed-column">
Name <h:inputText value="#{detailsBean.name}"
id="name" required="true"
requiredMessage="name required"/>
<rich:message for="name" ajaxRendered="true"/>
</h:panelGrid>
</h:body>
</h:form>
</html>
Now when I click on a link in my jsf page such a DetailsBean gets instantiated. When I click on another link with different content the same bean is used because I am still within the same Session. Now the strange thing is that even though I created 2 different browser tabs they show different content even after refreshing the page. How can the same bean instance show different contents ? I thought normally only a #ViewScoped bean could achieve this ? Don't get me wrong I DO want them to show different content so #ViewScoped would be the right decision to use here but I just wonder how this is possible...
EDIT2 : When I use javax.faces.ViewScoped, above Code doesn't work anymore (I get java.io.NotSerializableException because of the LinkedHashMap then)
I believe you have error in import line. import javax.enterprise.context.SessionScoped;. You should import annotations from javax.faces.bean.SessionScoped.
I have a dataTable. The data of the dataTable is filled via ajax. A row of the table contains among other things form elements like a button. The button in the datatTable should refer to another page but if I click on them the current page is reloaded.
Here some code:
the backing bean:
#ManagedBean(name="bean")
#SessionScoped
public class Bean {
private List<String> data;
#PostConstruct
public void postConstruct() {
data = new ArrayList<String>();
}
public void fillTable() {
data.add("E1");
data.add("E2");
data.add("E3");
}
public String outcome(){
return "/faces/test/edit.jsf";
}
public List<String> getData() {
return data;
}
public void setData(List<String> data) {
this.data = data;
}
}
the page:
<!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" >
<h:body>
<h:form id="form">
<h:commandButton value="fillTable">
<f:ajax listener="#{bean.fillTable()}" render="#form"/>
</h:commandButton>
<h:dataTable id="table" var="data" value="#{bean.data}">
<h:column>
<h:outputText value="#{data}" />
</h:column>
<h:column>
<h:commandButton value="edit" action="#{bean.outcome}" />
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
I know that this has something to do that the form is already in the dom and the button are lazy loaded into the page (if someone could be more specific I would be very pleased).
Although if I change the Scope of the backing bean to SessionScope it works. The button redirect to the right page. Why?
Although if I change the Scope of the backing bean to SessionScope it works. The button redirect to the right page
The bean should have been placed in the view scope. The session scope is too broad and would only risk unintuitive behaviour when the same view is been opened in multiple browser windows/tabs in the same session.
The explanation is as follows: when a form is submitted, JSF needs to identify the command button pressed in order to invoke the associated action method. As the command button is been placed in side a datatable, JSF needs to iterate over its datamodel first. But if the datamodel has been changed, or is empty, then JSF won't be able to identify the command button. Hence the action won't be invoked.
When the bean is in the request scope, then it will be trashed by end of response and recreated during every new request, including ajax requests. All of the bean's properties will obviously get the default values again, so also the data property in your case.
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - particularly point #4.
How to choose the right bean scope?