Retaining GET request query string parameters on JSF form submit - jsf

I have 3 pages:
main.xhtml
agreement.xhtml
generated.xhtml
The agreement.xhtml needs two parameters to load correctly: serviceId and site. So, a normal url looks like this: /app/agreement.xhtml?site=US&serviceId=AABBCC.
I have this button on agreement.xhtml
<h:form>
<h:commandButton value="Generate License File" action="#{agreement.generateMethod}" />
</h:form>
The #RequestScoped bean #{agreement} has this method:
public String generateMethod(){
.......
return "generated";
}
I need that, on click, the generateMethod() method is executed, and after it's done, the user is redirected to the generated.xhtml page. What's happening is that, on click, the page browser sends the user to /app/agreement.xhtml and, since it's not sending the parameters site and serviceId, it crashes.
I tried making the generateMethod() return a "generated?faces-redirect=true", but still nothing. Any ideas?

Your concrete problem is caused because a JSF <h:form> submits by default to the current request URL without any query string. Look closer at the generated HTML output, you'll see
<form action="/app/agreement.xhtml" ...>
You'd thus explicitly need to include those request parameters yourself. There are several ways to solve this. If you weren't sending a redirect, then you could just add them as hidden inputs to the JSF form.
<h:form>
<input type="hidden" name="site" value="#{param.site}" />
<input type="hidden" name="serviceId" value="#{param.serviceId}" />
...
</h:form>
Only, those parameters won't reappear in URL in browser's address bar. This isn't a problem if you're only using using ajax on the same page. The <h:inputHidden> is by the way not suitable as it will confusingly lose its value when a conversion or validation error occurs on the form.
In order to get them to reappear in URL, you need <f:viewParam> and includeViewParams. In order to get includeViewParams to work, you need to declare the following in both the source page agreement.xhtml ...
<f:metadata>
<f:viewParam name="site" value="#{agreement.site}" />
<f:viewParam name="serviceId" value="#{agreement.serviceId}" />
</f:metadata>
... and the target page generated.xhtml:
<f:metadata>
<f:viewParam name="site" value="#{generated.site}" />
<f:viewParam name="serviceId" value="#{generated.serviceId}" />
</f:metadata>
Now you can send a redirect including the view parameters as follows:
public String generateMethod() {
// ...
return "generated?faces-redirect=true&includeViewParams=true";
}
Do note that the bean should be #ViewScoped in order to keep those parameters alive between opening the page with the form and submitting the form, also on validation errors. Otherwise, when sticking to a #RequestScoped bean, you should be retaining them as <f:param> in the command components:
<h:commandButton ...>
<f:param name="site" value="#{generated.site}" />
<f:param name="serviceId" value="#{generated.serviceId}" />
</h:commandButton>
There's no way to set them for <f:ajax> inside input components, your bean should then really be #ViewScoped.
Alternatively, if you happen to use JSF utility library OmniFaces already, then you could also just replace the <h:form> by <o:form> as follows (see also showcase example):
<o:form>
That's basically all. This will generate a <form action> with current query string included.
<form action="/app/agreement.xhtml?site=US&serviceId=AABBCC" ...>
Those request parameters are then just available in the request parameter map of the form submit. You don't need additional metadata/viewparams and you also don't need to send a redirect and your bean can be kept #RequestScoped, if necessary.
public String generateMethod() {
// ...
return "generated";
}
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
How to navigate in JSF? How to make URL reflect current page (and not previous one)

Your generateMethod would have to return
return "generated?site=US&serviceId=AABBCC&faces-redirect=true";
You can even replace & with & but escape it in your xhtml.
In your generated.xhtml you can catch the parameters that are being passed with <f:viewParam> like this
<f:metadata>
<f:viewParam name="site" value="#{yourBean.site}"/><!--Make sure you have a setter-->
<f:viewParam name="serviceId" value="#{yourBean.serviceId}"/><!--Make sure you have a setter-
</f:metadata>
<h:head>

Related

Pass a parameter to dropzone save method using JSF

I have written the question about how to use dropzone.js with JSF (Use dropzone with JSF) and it was correctly answered by BalusC.
However I want to pass a parameter to the save() method and I am not able to do it. I have the dropzone component in a page similar to this:
http://localhost:8080/application/image-album.xhtml?albumId=1
And in that page I have:
<h:form id="uploadForm" enctype="multipart/form-data" styleClass="dropzone">
<div class="fallback">
<h:inputFile id="file" value="#{uploadImageController.part}"/>
<h:commandButton id="submit" value="submit" />
</div>
</h:form>
And in the UploadImageController I have:
#PostConstruct
public void init() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
albumId = Long.valueOf(externalContext.getRequestParameterMap().get("albumId"));
}
So theoretically I could use the albumId in the save() method. However the init() method is being called three times, the first time with the value 1 and the other two with the value null so it fails by NullPointerException.
Any workaround to this?
GET request parameters can be set as bean property using <f:viewParam>.
<f:metadata>
<f:viewParam name="albumId" value="#{bean.albumId}" />
</f:metadata>
If you're using a #ViewScoped bean, then this will be remembered across all JSF POST form submits (postbacks) on the same view. If you're however using a #RequestScoped bean, then bean properties can be retained across postbacks using <h:inputHidden>.
<h:form ...>
<h:inputHidden value="#{bean.albumId}" />
...
</h:form>
This only doesn't work properly when there's a conversion/validation failure on the form. If you're using conversion/validation on the very same form, then better use plain HTML <input type="hidden">.
<h:form ...>
<input type="hidden" name="albumId" value="#{bean.albumId}" />
...
</h:form>
The <f:viewParam> will take care that the bean property is properly set. There's no need to manually fiddle with getRequestParameterMap() the old fashioned JSF 1.x way.
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
How to retain f:viewParam values after postback with validation failed
How to choose the right bean scope?

Passing "get" parameters doesn't work, parameter not visible in the link

I'm a beginner to JSF and I want to code a little searchbar on my future website.
I made two pages : index.xhtml and search.xhtml, and I try to pass get parameters from index.xhtml to search.xhtml, so I made this little formular :
<!-- index.xhtml -->
<h:form id="Form_search">
<h:inputText class="search_bar_text" binding="#{se}"></h:inputText>
<h:button class="search_bar_button" outcome="search">
<f:param name="search" value="#{se.value}" />
</h:button>
</h:form>
To summarize, I want to send the content of an inputText to search.xhtml
But there's a problem : when I click on the submit button, no parameters are passed, so instead of having /search.xhtml?search=foobar I only have /search.xhtml.
I also tried this, but this doesn't work either :
<!-- index.xhtml -->
<h:form id="Form_search">
<h:inputText class="search_bar_text" binding="#{se}"></h:inputText>
<h:button class="search_bar_button" outcome="search.xhtml?search=#{se.value}">
</h:button>
</h:form>
Can someone explain to me the reason of this problem and how I can fix it?
The <f:param value> and <h:button outcome> are evaluated during rendering the HTML output, not during "submitting" of the form as you seem to expect. Do note that there's actually no means of a form submit here. If you're capable of reading HTML code, you should see it in the JSF-generated HTML output which you can see via rightclick, View Source in webbrowser.
Fix it to be a true GET form. You don't need a <h:form>, <h:inputText>, nor <h:button> here at all. You don't want a POST form. You don't seem to want to bind the input to a bean property. You don't want a plain navigation button.
<form id="form_search" action="search.xhtml">
<input name="search" class="search_bar_text" />
<input type="submit" class="search_bar_button" />
</form>
Yes, you can just use plain HTML in JSF.
If you really, really need to use JSF components for this purpose for some reason, then you could also use this POST-redirect-GET-with-view-params trick.
First add this to both index.xhtml and search.xhtml:
<f:metadata>
<f:viewParam name="search" value="#{bean.search}" />
</f:metadata>
Then use this form:
<h:form id="form_search">
<h:inputText value="#{bean.search}" styleClass="search_bar_text" />
<h:commandButton styleClass="search_bar_button" action="search?faces-redirect=true&includeViewParams=true" />
</h:form>
This would perhaps make sense if you intend to use JSF validation on it. But even then, this doesn't prevent endusers from manually opening the URL with invalid params. You'd then better add validation to <f:viewParam> itself on search.xhtml.
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for? (scroll to bottom of answer)
How do I process GET query string URL parameters in backing bean on page load?

f:param does not work with p:commandLink or h:commandLink on query string

f:param works great with h:link, but not with p:commandLink or h:commandLink.
For example, I have two pages test_first.xhtml and test_second.xhtml, and a backing java bean TestBean.java.
I start running test_first.xhtml.
If I click link1, which is a h:link, the page will redirect to test_second.xhtml. With the help of f:param, the address bar of the browser will show .../test_second.xhtml?id=1. On that page, testBean.userId gets printed.
If I click link2 or link3, the page redirects to test_second.xhtml. However, the address bar only shows .../test_second.xhtml, there is NO ?id=#! And testBean.userId does not get printed on that page.
How can I make commandLink work with f:param? Sometimes I want the link not to redirect to another page but to call some methods of bean depending on the data.
test_first.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head/>
<h:body>
<h:form>
<h:link value="link1" outcome="test_second" >
<f:param name="id" value="1"/>
</h:link>
<br/><br/>
<h:commandLink value="link2" action="test_second?faces-redirect=true" >
<f:param name="id" value="2" />
</h:commandLink>
<br/><br/>
<p:commandLink value="link3" action="test_second?faces-redirect=true">
<f:param name="id" value="3" />
</p:commandLink>
<br/><br/>
</h:form>
</h:body>
</html>
test_second.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<f:metadata>
<f:viewParam name="id" value="#{testBean.userId}" />
</f:metadata>
<h:head/>
<h:body>
<h:form>
This is the second page.
<h:outputText value="Selected id is #{testBean.userId}" />
<h:commandButton value="Print page id" action="#{testBean.print()}" />
</h:form>
</h:body>
</html>
TestBean.java
#ManagedBean
#SessionScoped
public class TestBean implements Serializable{
private Integer userId;
public void print() {
System.out.println(userId);
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
}
You misinterpreted the meaning of those two tags, namely <h:link> and <h:commandLink>, therefore, you also misinterpreted the meaning of <f:param> attached to either of the two. In anycase it is worthwhile to always read the documentation before asking the questions to get more insight.
<h:link> renders an HTML "a" anchor element. The value of the component is rendered as the anchor text and the outcome of the component is used to determine the target URL rendered in the "href" attribute. Any child UIParameter components are appended to the String to be output as the value of the "href" attribute as query parameters before rendering...
<h:commandLink> render an HTML "a" anchor element that acts like a form submit button* when clicked ... if the disabled attribute is not present, or its value is false. It renders "#" as the value of the "href" attribute, renders the current value of the component as the link text if it is specified and *renders JavaScript that is functionally equivalent to the following as the value of the "onclick" attribute:
document.forms['CLIENT_ID']['hiddenFieldName'].value='CLIENT_ID';
document.forms['CLIENT_ID']['PARAM1_NAME'].value='PARAM1_VALUE';
document.forms['CLIENT_ID']['PARAM2_NAME'].value='PARAM2_VALUE'; return false;
document.forms['CLIENT_ID'].submit()"
where hiddenFieldName is as described above, CLIENT_ID is the clientId of the UICommand component, PARAM_NAME and PARAM_VALUE are the names and values, respectively, of any nested UIParameter children.
In other words, within <h:link> tag nested <f:param> will end up as a query parameter of the generated URL, while within <h:commandLink> tag nested <f:param> will end up as a request parameter with a given value.
While the first one is clear, the second one deserves a better elaboration. To understand what it does, consider that if we abstract away from the details <h:commandLink> sends a POST request and attaches all nested <f:param> tags as request parameters. But it is up to you how you will handle them, as navigation is entirely in your hands.
So, the first option is to set a hardcoded action attribute, which use case is dubious, like in action="second-page", in which way you didn't pass any query parameter at all. What will be done is POSTing to the same view and forwarding to the second without undertaking any action. Quite a dumb action.
The second option is to specify an action method, like in action="#{bean.action}". In this case you must handle navigation in the provided action method, i.e. return null/void from the method for a postback, or return a navigation case outcome as a string to make a forward to the specified view. As for the request parameters that you passed with <f:param> they will be available with standard JSF means like #ManagedProperty("#{param.name}") on a request-scoped bean, or by calling ExternalContext#getRequestParameterMap() in any-scoped bean, for example, in action method, like in String param = externalContext.getRequestParameterMap().get("name"). So now you have your parameter in action method that you're free to use how you like, just adhere to a set of rules that exist for URLs.
Two things left worth mentioning. Remember that request parameters passed with calling the command link will be available only within that same request, as you might expect it to survive a faces-redirect=true that basically fires another request. The other option is to specify includeviewparams=true to pass through the paramaters of the current view, if that's desired, as mentioned in the other answer.
You could do it by concatenating the parameters with & directly at the action attribute:
<p:commandLink value="link3" action="test_second?faces-redirect=true&id=3"/>
Update 1
You might also consider to add &includeViewParams=true. This way view parameters of your target navigation will be included automatically.

View parameter when navigating to another page

I am using JSF2, and I need to be able to pass a parameter from one JSF page to another via a commandLink.
I am on page funding.xhtml (ViewScoped) and have the following link defined:
<p:commandLink styleClass="toolbar"
action="/application/customerApplicationManagement.jsf">
<p:graphicImage url="/resources/gfx/search.png" />
<h:outputText value="#{msg.menu_searchApplications}" styleClass="toolbarLink" />
</p:commandLink>
I need to pass a string value to the customerApplicationManagement page indicating which page I came from so that after selecting an application, I can return to that page. I have tried several suggestions about how to pass this value including f:param, f:viewParam. I have even tried just adding it directly to the url (?fromPage=funding) etc, but they all seem to work only when the value is passed back to the current page, not a new page I am navigating to.
Can someone show me how this can best be accomplished.
Use <f:param> and <f:viewParam>:
Source page:
<p:commandLink styleClass="toolbar"
action="/application/customerApplicationManagement.jsf">
<p:graphicImage url="/resources/gfx/search.png" />
<h:outputText value="#{msg.menu_searchApplications}" styleClass="toolbarLink" />
<f:param name="fromPage" value="funding.xhtml" />
</p:commandLink>
Destination page (bound):
<f:metadata>
<f:viewParam name="fromPage" value="#{destinationBacking.fromPage}" />
</f:metadata />
<h:link value="Go back!" outcome="#{destinationBacking.fromPage}" />
Destination page (unbound):
<f:metadata>
<f:viewParam name="fromPage" />
</f:metadata />
<h:link value="Go back!" outcome="fromPage" />
Backing bean (only if you want to bind the param):
#ManagedBean
#ViewScoped
public class DestinationBacking{
String fromPage;
public String getFromPage(){
return fromPage;
}
public void setFromPage(String frompage){
fromPage = frompage;
}
}
Your view path will be binded to fromPage property from the destination backing bean and after you can use it to return to the original page.
Also I want to say that this way is a bit 'hackeable' by the end user, I mean, you're passing the original path through pure url. See also other ways to achieve that, as flash scope, which is very useful specially if you're working with #ViewScoped beans.
I don't know the specifics of the methods you tried to achieve your goal and hence we cant tell what was wrong with them, but if we consider your code 'as is' you don't have anything that will pass the string you want.
Not to repeat ourselves, there are plenty of answers here dedicated to using this or that method, so I will give you the best references, in my opinion, of course.
How can I pass a parameter to a commandlink inside a datatable;
ViewParam vs #ManagedProperty;
What can <f:metadata> and <f:viewParam> be used for.
Regarding the usage of back buttons in JSF you could also take a look at my own answer on How to get back to the same page in JSF.
By the way, using POST for page-to-page navigation is considered to be a bad practice. If all you need is to navigate to another page you'd better use plain <h:link> or <h:button> instead.

Automatically include view parameters in URL

I have simple jsf page with view params and load method which is processing those params:
<f:metadata>
<f:viewParam name="param1" value="#{bean.param1}"/>
<f:viewParam name="param2" value="#{bean.param2}"/>
<f:viewParam name="param3" value="#{bean.param3}"/>
<f:event type="preRenderView" listener="#{bean.load()}"/>
</f:metadata>
I also set some initial values in #PostConstruct.
How to redirect user to new location that include those parameters (which are not null).
For example user enter in browser:
domain.com/page.jsf
and is redirected to:
domain.com/page.jsf?param1=valueA
because param1 was set in #PostConstruct.
Another question - I have links on page referencing same view:
<h:link value="clickme">
<f:param name="param3" value="otherValue"/>
</h:link>
When user enters page with ?param1=someValue and clicks link, got redirected to ?param3=otherValue but I want to redirect to ?param1=someValue&param3=otherValue.
I know I can add more parameters in <h:link> but it's diffucult to add every possible param in every <h:link>
PS. I use BalusC tip from this topic JSF 2 and Post/Redirect/Get?
As to the 1st question: you can add includeViewParams=true to the navigation case outcome. But you can never guarantee that you'll sucessfully be redirected while you're doing that inside a preRenderView method. It might be already too late then.
As to the 2nd question: you can set the includeViewParams attribute of <h:link> to true.
<h:link value="clickme" includeViewParams="true">
<f:param name="param3" value="otherValue"/>
</h:link>
Alternatively, you can also add includeViewParams=true to the outcome.
<h:link value="clickme" outcome="otherPage?includeViewParams=true">
<f:param name="param3" value="otherValue"/>
</h:link>

Resources