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>
Related
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 found a weird behavior in JSF/PrimeFaces and ask your help to understand and get around it. The actionListener method in a commandButton is executed just once.
Contextualization:
I put a link in start page of my project to a second page, renderized as follows:
http://localhost:8080/MeusTestes-war/faces/somepage.xhtml?id=1
Notice there is a parameter sent by query string.
The somepage.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:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Facelet Title</title>
</h:head>
<f:metadata>
<f:viewParam name="id" value="#{someBean.id}" required="true" />
<f:viewAction action="#{someBean.init}" />
</f:metadata>
<h:body>
<h:form id="form1">
<p:commandButton id="teste1"
value="Teste"
actionListener="#{someBean.doTeste}" />
</h:form>
</h:body>
</html>
As you can see, its extremely simple. Notice there is a metadata section doing the parameter reception and the execution of init() method. In page body there is a p:commandButton and a actionListener pointing to doTeste().
There is my Bean:
#Named(value = "someBean")
#ViewScoped
public class SomeBean implements Serializable {
private int id;
public SomeBean() {
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public void init() {
System.out.println("init " + id);
}
public void doTeste(ActionEvent actionEvent) {
System.out.println("doTeste " + id);
}
}
Well, now the mysterious behavior:
1) When page is loaded, as expected, the init() method shows a message with the correct property value got by viewParam.
2) Firing the button, as expected too, the doTeste() method shows a message with the correct property value.
However,
3) Firing the button again, nothing happens!
Other facts:
If I remove the metadata section the doTeste() method is executed as many times as the button is clicked, which is supposed to happen. But the property, obviously, is not initialized.
If I switch the button definition from p:commandButton to h:commandButton, the doTeste() method is executed as expected AND the property is initialized. But I lose the PrimeFaces pattern.
My question:
How to do the commandButton actionListener from PrimeFaces behave the way is expected? (executing the method each time it is fired)
Thanks!
If you for testing purposes add a <p:growl id="msgs"/> and add update="msgs" to the button you'll see that validation fails on subsequent requests (because of required="true" on the viewParam).
So you can either
remove required="true". Probably a bad idea since you need it.
add the parameter to the commandButton with <f:param name="id" value="#{someBean.id}"/>
use OmniFaces <o:viewParam>.
There is a more technical explanation here.
This may have been covered somewhere but I'm having trouble forming the question for search engine and no goods leads thus far.
I'm working on a page that acts as entity view. Lots of results come from database and only a handful are displayed at a time. So you can imagine that I want to build a list of links that take user to another page of entities. This is all my code - no PrimeFaces or any other front-end nifty pagination solutions. At least for now.
To the code:
#Named
#SessionScoped
public class ArticleIndexBean {
List<Article> articleList=new ArrayList<>();
List<Article> articleSubList=new ArrayList<>();
#PostConstruct
public void loadScreenSupport() {
search();
toEntityPage(1);
}
protected void search() {
// this method sets articleList which is the full list fetched from the database
}
public void toEntityPage(int pageNumber) {
// this method sets articleSubList which is a subset of articleList
}
Each page link needs to call toEntiyPage(n). I am aware of commandLink but I want to avoid a POST request. Also, the bean is currently session scoped and I will try to make it conversation scoped later. It will certainly NOT be request scoped, as I don't want to do a full db search each time a user wants to jump to another page. So #PostConstruct won't help, either.
So with a menu like this: 1 * 2 * 3 * 4 * 5 how do I code an outputLink or any other type of link that will call my ArticleIndexBean.toEntityPage(int) via a GET request?
Solution
Based on input from Laurent, I added a currentEntityPageNumber property and a toCurrentEntityPage() method to my bean. The toCurrentEntityPage() simply calls toEntityPage(getCurrentEntityPageNumber()).
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<f:metadata>
<f:viewParam name="pn" value="#{articleIndexBean.currentEntityPageNumber}" />
<f:event type="preRenderView" listener="#{articleIndexBean.toCurrentEntityPage()}" />
</f:metadata>
<c:forEach var="pageNumber" begin="1" end="${articleIndexBean.getEntityPageCount()}">
<h:outputLink value="ar_index.xhtml">
<h:outputText value="${pageNumber}" />
<f:param name="pn" value="${pageNumber}" />
</h:outputLink>
</c:forEach>
It would certainly be better if we could call toEntityPage(pageNumber) directly but this works fine.
Assuming you are using JSF 2.2, you could use the viewParam to retrieve the page in the GET parameters and viewAction to call a method before the rendering (actually called in the INVOKE_APPLICATION phase by default).
Facelets:
<f:metadata>
<f:viewParam name="page" value="#{articleIndexBean.entityPage}" />
<f:viewAction action="#{articleIndexBean.loadScreenSupport}" />
</f:metadata>
If you are using JSF 2.0 or JSF 2.1, then you have to replace viewAction by:
<f:event type="preRenderView" listener="#{articleIndexBean.loadScreenSupport}" />
Java:
#Named
#SessionScoped
public class ArticleIndexBean {
List<Article> articleList=new ArrayList<>();
List<Article> articleSubList=new ArrayList<>();
int pageNumber = 1; // by default first page
public void loadScreenSupport() {
search();
toEntityPage(pageNumber);
}
public int getPageNumber() {
return pageNumber;
}
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
protected void search() {
// this method sets articleList which is the full list fetched from the database
}
public void toEntityPage(int pageNumber) {
// this method sets articleSubList which is a subset of articleList
}
}
The link to the page is then easy:
<h:outputLink value="resultPage.xhtml">
<h:outputText value="2" />
<f:param name="page" value="2" />
</h:outputLink>
Reference:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
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.
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?