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?
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 have a simple Form in my JSF page that looks as follows:
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
>
<f:view>
<h:head>
<f:metadata>
<f:viewParam name="cities" value="#{myBean.cities}" />
</f:metadata>
</h:head>
<h:body>
<h:form>
<h:messages id="messages" />
<h:selectManyCheckbox value="#{myBean.cities}" label="Select City" >
<f:selectItems value="#{myBean.allCities}" />
</p:selectManyCheckbox>
<h:commandButton value="Submit">
<f:ajax execute="#form" render="output"/>
</h:commandButton>
</h:form>
<h:panelGroup id="output">
Number of Selected Cities: #{myBean.cities.size()}
</h:panelGroup>
</h:body>
</f:view>
</html>
The matching backing bean:
With following bean methods:
#Named
#RequestScoped
public class MyBean {
private List<String> cities = new ArrayList<>();
public List<String> getCities() {
return cities;
}
public void setCities(List<String> cities) {
this.cities = cities;
}
public List<String> getAllCities() {
return new ArrayList<>(Arrays.asList(new String [] {
"Los Angeles",
"St. Louis",
"San Francisco",
"Dallas",
}));
}
}
Observations:
I have added logging to the entry and exit of the getCities and setCities methods. During refresh of the JSF page, the getCities method gets called. However, when submitting, the setCities never gets called.
I do not get any errors or exceptions in the console log (javax.faces.PROJECT_STAGE set to DEVELOPMENT in web.xml).
No errors are being passed to <h:messages/>
set methods do not get called for any form input fields. The page behaves 'odd'.
The issue seems similar to Issue #3 on the accepted answer from commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated, however I do not have an apparent conversion problem for <p:selectCheckboxMenu>. As per documentation it should be able to handle a List<String> perfectly.
So in addition to fixing the obvious problem, how can we make sure any errors related to this are made visible, rather than having this silent type of failure?
In short, <f:viewParam> does not yet support list of values.
The signature of the cities property is List<String>.
Note that when we are trying to pass a parameter for cities (append ?cities=Dallas to the end of the request URL), we suddenly do get an appropriate conversion error in <h:messages/> stating:
Conversion Error setting value 'Dallas' for 'null Converter'.
Also as per referenced posts, we should also include the messages for ajax updates:
<h:commandButton value="Submit">
<f:ajax execute="#form" render="output"/>
<f:ajax execute="#form" render="messages"/>
</h:commandButton>
When following this, not during first visit, but at least during submit we get an appropriate error:
Conversion Error setting value '[]' for 'null Converter'.
I am not sure why the <f:viewParam>'s need to be set during an ajax-style submit though?
To solve, you can either provide a <f:converter>, or provide additional getter/setters that handles setting/getting the Array or List type based on a String.
Picking a quick solution we can change the <f:viewParam> as follows:
<f:viewParam name="cities" value="#{myBean.citiesCsv}" />
While for the backing bean, we add following method:
public void setCitiesCsv(String csv) {
if (csv.isEmpty()) {
cities = new ArrayList<>();
} else {
cities =
Stream.of(csv.split(","))
.collect(Collectors.toCollection(ArrayList<String>::new));
}
}
For repeated such efforts, we should probably consider an appropriate converter to be used instead, which we can custom build for simple CSV conversions if it suits our purpose.
Related posts
commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated (Item #3)
Distinguish between conversion failure and validation failure in o:viewParamValidationFailed
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
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?
I'm new to JSF and trying to understand how include and param work, but have been stuck. Any help is much appreciated.
I have 2 simple pages (for testing purpose), Page1.xhtml and Page2.xhtml. I want to include Page2 into Page1 with one parameter using and in Page1. When I call Page2 directly, I can see the parameter being passed properly, but when I call Page1, Page2 is being included without the parameter. Below is the code.
Page1:
<h:body>
<h:form id="test">
<b>Page 1</b><br/>
<ui:include src="Page2.xhtml">
<ui:param name="id" value="123" />
</ui:include>
<b>End of Page 1</b>
</h:form>
Page2:
<h:head>
<f:view contentType="text/html"></f:view>
</h:head>
<h:body>
<h:form>
<h:outputLabel for="ID" value="ID on Page2: "/>
<h:outputText id="ID" value="#{pageTestBean.id}"/>
</h:form>
</h:body>
</html>
PageTestBean:
#ManagedBean
#SessionScoped
public class PageTestBean {
private Long id=new Long(11111);
public void init() {
//doesn't do anything yet;
}
// Getters and Setters
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
I'm expecting to see "123" as the output Id on Page1, not "11111" which is the default value when no parameter is passed in. However, I always see 11111. Is my expectation wrong?
First of all, your include is handled inappropriately: the incuded page should be composed solely of <ui:composition> like the following one:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets">
...incuded page content...
</ui:composition>
An excellent point of reference is BalusC's answer to How to include another XHTML in XHTML using JSF 2.0 Facelets?.
Next, the included parameter is to be accessed simply via #{paramName} in the included page, like in:
<h:outputText value="#{paramName}" />
Parameter name is id in your case.
There are some other drawbacks of your code, like abusing session scope and nested HTML forms, but that's another question. The last but not the least is the thing that you have to understand how to deal with managed beans in views.
This question already has answers here:
Retaining GET request query string parameters on JSF form submit
(2 answers)
Closed 6 years ago.
I'm facing the following issue: in one page, I list all users of my application and have an "edit" button for each one, which is a "GET" link with ?id=<userid>.
The edit page has a <f:viewParam name="id" value="#{editUserBean.id}"/> in metadata.
If I made some input mistakes and submit (I use CDI Weld Bean validation), the page is displayed again, but I've lost the ?id=... in the URL and so lose the user id of the user I'm editing.
I've looked at a similar problem described in JSF validation error, lost value, but the solution with inputhidden (or worse, with tomahawk, which looks overkill) requires lot of uggly code.
I've tried adding a "Conversation" with CDI, and it is working, but it looks like too much overkill to me again.
Does there exists a simple solution in JSF to preserve view parameters in case of validation errors?
[My environment: Tomcat7 + MyFaces 2.1.0 + Hibernate Validator 4.2.0 + CDI(Weld) 1.1.2]
Interesting case. For everyone, the following minimal code reproduces this:
Facelet:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
>
<f:metadata>
<f:viewParam id="id" name="id" value="#{viewParamBean.id}"/>
</f:metadata>
<h:body>
<h:messages />
#{viewParamBean.id} <br/>
<h:form>
<h:inputText value="#{viewParamBean.text}" >
<f:validateLength minimum="2"/>
</h:inputText>
<h:commandButton value="test" action="#{viewParamBean.actionMethod}"/>
</h:form>
</h:body>
</html>
Bean:
#ManagedBean
#RequestScoped
public class ViewParamBean {
private long id;
private String text;
public void actionMethod() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
If you call the Facelet with viewparam.xhtml?id=12 it will display the 12 onscreen. If you then input something valid, e.g. aaaaa, the id will disappear from the URL, but keeps being displayed on screen (owning to the stateful nature of ui components).
However... as OP mentioned, as soon as any validator error occurs (e.g. entering a), the id will be permanently lost. Entering valid input afterwards will not bring it back. It almost seems like a bug, but I tried both Mojarra 2.1 and Myfaces 2.1 and both have the same behavior.
Update:
After some inspection, the problem seems to be in this method of `UIViewParameter' (Mojarra):
public void encodeAll(FacesContext context) throws IOException {
if (context == null) {
throw new NullPointerException();
}
// if there is a value expression, update view parameter w/ latest value after render
// QUESTION is it okay that a null string value may be suppressing the view parameter value?
// ANSWER: I'm not sure.
setSubmittedValue(getStringValue(context));
}
And then more specifically this method:
public String getStringValue(FacesContext context) {
String result = null;
if (hasValueExpression()) {
result = getStringValueFromModel(context);
} else {
result = (null != rawValue) ? rawValue : (String) getValue();
}
return result;
}
Because hasValueExpression() is true, it will try to get the value from the model (the backing bean). But since this bean was request scoped it will not have any value for this request, since validation has just failed and thus no value has ever been set. In effect, the stateful value of UIViewParameter is overwritten by whatever the backing bean returns as a default (typically null, but it depends on your bean of course).
One workaround is to make your bean #ViewScoped, which is often a better scope anyway (I assume you use the parameter to get a user from a Service, and it's perhaps unnecessary to do that over and over again at every postback).
Another alternative is to create your own version of UIViewParameter that doesn't try to get the value from the model if validation has failed (as basically all other UIInput components do).
You don't actually loose the view parameter. f:viewParam is stateful, so even if it's not in the URL, it's still there. Just put a break point or system.out in the setter bound to view param.
(if you google on viewParam stateless stateful you'll find some more info)
I've the same in my Application. I switched to #ViewAccessScoped which allows way more elegant implementations.
<f:metadata>
<f:viewParam id="id" name="id" value="#{baen.id}"/>
</f:metadata>
Or when you the first time get parameter from url, save it in session map and continue use from that map, and after save/or update the form clean map.
This is tricky, but you can try to restore view parameters with History API:
<?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"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<f:metadata >
<f:viewParam name="param1" value="#{backingBean.viewParam1}" />
<f:viewParam name="param2" value="#{backingBean.viewParam2}" />
<f:viewAction action="#{view.viewMap.put('queryString', request.queryString)}" />
</f:metadata>
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<ui:fragment rendered="#{facesContext.postback}" >
<script type="text/javascript">
var url = '?#{view.viewMap.get('queryString')}';
history.replaceState({}, document.title, url);
</script>
</ui:fragment>
<h:form>
<h:inputText id="name" value="#{backingBean.name}" />
<h:message for="name" style="color: red" />
<br />
<h:commandButton value="go" action="#{backingBean.go}" />
</h:form>
<h:messages globalOnly="true" />
</h:body>
</html>