Does f:viewParam only pass query string in url when first page uses the same managed bean as the second page? - jsf

Let's use a search page and a results page for example. If i have a ViewScoped bean that handles my search page and my results page, i'm able to pass parameters through the url using something like this:
search.xhtml
<p:commandButton value="Search" type="submit" action="#{searchBacker.search}" >
backing bean
#ManagedBean(name="search")
#ViewScoped
public class searchBacker extends AbstractBacking {
private String firstName = null;
private String lastName = null;
private String results = null;
public String search() {
return "results?faces-redirect=true&includeViewParams=true";
}
public void getResults() {
MyDAO dao = new MyDAO();
results = dao.getResults(firstName, lastName);
}
//getters and setters
}
results.xhtml
<f:metadata>
<f:event type="preRenderView" listener="#{searchBacker.getResults}" />
<f:viewParam name="firstName" value="#{searchBacker.firstName}"/>
<f:viewParam name="lastName" value="#{searchBacker.lastName}"/>
</f:metadata>
Now lets say i have two managed beans - one for the search page and one for the results page.
Will the query string still be built in the url with 2 different managed beans or does this only work when using the same managed bean for both pages?
UPDATE
I have the same <f:viewParam> on my search.xhtml and results.xhtml page, but the only difference is that my f:viewParam value points to a different backer in my results.xhtml than in my search.xhtml. When i do this, no params get passed through the url. When I point my f:viewParam value in results.xhtml to the same backer that i use in search.xhtml, the param gets passed through the url just fine, but the value doesn't exist in the results backer where i need it. If i have duplicate f:viewParams in my results.xhtml page - one with the search backer and one with the results backer, everything works fine. Is having 2 of the same f:viewParams with both managed beans the correct way to do this?
Examples:
results.xhtml - params get passed through url, but are not available in my resultsBacker
<f:metadata>
<f:viewParam name="firstName" value="#{searchBacker.firstName}"/>
<f:viewParam name="lastName" value="#{searchBacker.lastName}"/>
</f:metadata>
results.xhtml - no params get passed through url
<f:metadata>
<f:viewParam name="firstName" value="#{resultsBacker.firstName}"/>
<f:viewParam name="lastName" value="#{resultsBacker.lastName}"/>
</f:metadata>
results.xhtml - params get passed through url and are available in my resultsBacker, but seems clunky. Is this the correct way to do this or am i still missing something?
<f:metadata>
<f:viewParam name="firstName" value="#{searchBacker.firstName}"/>
<f:viewParam name="firstName" value="#{resultsBacker.firstName}"/>
<f:viewParam name="lastName" value="#{searchBacker.lastName}"/>
<f:viewParam name="lastName" value="#{resultsBacker.lastName}"/>
</f:metadata>

I suggest you to implement two #ViewScoped managed beans, one for the search page and the other for the results page. You should also have two xhtml pages, each one related with a bean. Obviusly its page will have its own url (as it seems you're doing right now with the same bean).
You can make the second page's bean expect two parameters, firstName and lastName. After, in the preRenderView method, when parameters are already set into the second managed bean, query your DB with that values. So how to achieve the transition between the beans? An outcome should be enough.
<p:button outcome="results">
<f:param name="firstName" value="#{searchBean.firstName}">
<f:param name="lastName" value="#{searchBean.lastName}">
</p:button>
This makes JSF build an url with the params you need. After you can do exactly the same that you're doing right now, but using your second bean to get your params (I strongly suggest you not to use a getter method for the preRenderView method):
<f:metadata>
<f:event type="preRenderView" listener="#{resultBacker.initialize}" />
<f:viewParam name="firstName" value="#{resultBacker.firstName}"/>
<f:viewParam name="lastName" value="#{resultBacker.lastName}"/>
</f:metadata>

includeViewParams works only if both the source and target view have the desired view parameters declared as <f:viewParam>.
So, in your parituclar case, you need to put the <f:viewParam>s with very same name in both the search.xhtml and results.xhtml. Under the covers, the includeViewParams namely only grabs the view params of the current view and then only applies the ones which are also declared in the target view.
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Unrelated to the concrete problem, you seem to effectively want a GET form. In that case, there is a better way for that than performing a POST-Redirect-GET with parameters. Look at the bottom of the above "See also" link.

Related

Primefaces Dialog Framework and Parameters [duplicate]

I have a product.xhtml and a ProductBean. I use /product/{id} to access the products so I have a viewParam in product.xhtml with value=ProductBean.id. The problem is that inside the bean I use an init function with a PostConstruct annotation in order to fill the details of the product. To do this I need the id to call an external function. I guess though that init is called before viewParam sets the id of the bean and therefore inside init I cannot call the external function because id is not set yet. What am I doing wrong and how do I fix this?
UPDATE
I found what was wrong. I think the viewParam method works with CDI beans but the ManagedProperty method works with JSF beans..
I do have one other problem now. My CDI bean is RequestScoped and when the product.xhtml is rendered the bean is created and I guess is later discarded. The funny thing is that I have a function inside that bean which when I call, I can read the id (which I assume this happens because is connected to the view param) but not any other properties. Any ideas how to fix this?
You need a <f:event type="preRenderView"> instead.
<f:metadata>
<f:viewParam name="foo" value="#{bean.foo}" />
<f:event type="preRenderView" listener="#{bean.onload}" />
</f:metadata>
With
public void onload() {
// ...
}
Note that this is in essence a little hack. The upcoming JSF 2.2 will offer a new and more sensible tag for the sole purpose: the <f:viewAction>.
<f:metadata>
<f:viewParam name="foo" value="#{bean.foo}" />
<f:viewAction action="#{bean.onload}" />
</f:metadata>
See also:
ViewParam vs #ManagedProperty(value = "#{param.id}")
Communication in JSF 2.0 - Processing GET request parameters

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?

lose view page parameter

i have page my.xhtml:
<f:metadata>
<f:viewParam name="id"/>
</f:metadata>
...
<h:form>
Current id is: "#{id}"
<h:commandButton action="#{bean.doSomething}" value="Do some logic..">
<f:param name="id" value="#{id}"/>
</h:commandButton>
</h:form>
and Bean.java:
#ManagedBean
#ViewScoped
public class Bean {
....
public void doSomething(){
//do some logick, don't use id parameter
}
}
When I get to page first time with id=10 I can see on page Current id is: "10"..
When I click on button page is reload and I can see on page Current id is: "10". again.
But when I click on button third time I can see on page Current id is: ""., i lose id parameter and I don't understand why?
I know I can do this with save parameter value in bean
(by add to f:viewParam:
<f:metadata>
<f:viewParam name="id" value="#{bean.value}/>
</f:metadata>
), but can I do this without save parameter value in bean?
h:button works with f:param but h:commandButton does not. In this case, your best is to bind the view parameter to a bean property as you explain the last. The #ViewScoped bean will retain its state as long as you call void action methods.
You could also pass it as a parameter to the action method (there are several ways to do that), but this doesn't make sense for a method which has nothing to do with that value.
See also:
Passing parameters to action methods in JSF
Communication in JSF

Retaining GET request query string parameters on JSF form submit

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>

View parameters in different beans

I've got a page called trip.xhtml where I take parameters out of the URL using the following code:
<f:metadata>
<f:viewParam name="tripid" value="#{tripBean.tripId}" />
<f:viewParam name="seats" value="#{tripBean.seats}" />
<f:event type="preRenderView" listener="#{tripBean.processParams}" />
</f:metadata>
The TripBean looks like this (simplified):
#ManagedBean
#RequestScoped
public class TripBean implements Serializable {
private static final long serialVersionUID = -4885781058258859229L;
private Long tripId;
private int seats;
// Getters and setters for tripId and seats
On the bottom of the trip.xhtml page I have a h:link:
<h:link outcome="/customer/booking.xhtml" value="Book this trip"
includeViewParams="true" />
What I expect is that the URL I get when I click this link is something like "/customer/booking.jsf?tripid=2&seats=1". This is only the case when I put the following code on my booking.xhtml page:
<f:metadata>
<f:viewParam name="tripid" value="#{tripBean.tripId}" />
<f:viewParam name="seats" value="#{tripBean.seats}" />
</f:metadata>
Although what I actually want is to use another bean. Changing the code to:
<f:metadata>
<f:viewParam name="tripid" value="#{bookingBean.tripId}" />
<f:viewParam name="seats" value="#{bookingBean.seats}" />
</f:metadata>
The BookingBean also has 2 properties tripId and seats which are identical to the TripBean, but when I try to click the link now, I only see a seats-parameter which is set to 0. ("/customer/booking.jsf?seats=0")
Does anyone have any idea why I can't seem to pass the viewparams to the other page when I'm trying to use another bean to store them in? And IF it is impossible to store it in another bean, how can I put those values from TripBean in BookingBean?
Quick work-around I used:
Not using includeViewParams="true" , but adding parameters to the link manually (see below) "fixes" the problem. Although I'm still wondering why it won't work with includeViewParams!
<h:link outcome="/customer/booking.xhtml" value="#{msg['page.trip.booking']}">
<f:param name="tripid" value="#{tripBean.tripId}" />
<f:param name="seats" value="#{tripBean.seats}" />
</h:link>
f:viewParam works exactly like h:inputText. This means that it uses the same expression as a source (when rendering) and as a target (when updating the model). If you had:
<h:inputText value="#{a.test}" />
You would never ask "how to make the inputText read from b.foo and write to a.test", yet everyone seems to expect such behavior from f:viewParam.
Anyway, there is a simple answer. To append any values to link, you just need to use f:param:
<h:link outcome="/customer/booking.xhtml" value="Book this trip" >
<f:param name="tripid" value="#{bookingBean.tripId}" />
<f:param name="seats" value="#{bookingBean.seats}" />
</h:link>
The link content corresponding to the following tag:
<h:link outcome="/customer/booking.xhtml" value="Book this trip"
includeViewParams="true" />
is generated during the render-response phase of the request for the trip.xhtml view. If you need the URL to contain the view parameter values, then you must provide these values in the URL used to access the view, or you must set these values in the model, before the render-response phase is complete. If you do not perform either of these, the generated hyperlink will contain the default values for the view parameters.
In simpler words, you must:
either ensure that the trip.xhtml view is accessed with the necessary parameters: trip.xhtml?tripid=x&seats=y, especially if the view is to be accessed with parameters at all times.
or you must set the values in a method that gets executed before the link is rendered by the JSF runtime. The constructor of the BookingBean class or a #PostConstruct annotated method in the class would be ideal places to set the values. You could also reset/update the values of the bean in other methods of your managed bean, in the event of actions being performed in the view. Note, that JSF runtime will invoke getTripId and getSeats to include the values of the view parameters tripId and seats in the resulting URL, so you ought to verify the behavior of these methods as well.
Your question on why you are unable to specify bookingBean in the EL expression for booking.xhtml, instead requiring you to specify the supposedly unexpected value of tripBean is due to the fact that the includeViewParams attribute of the link tag, will include the parameters of the to-view and not the from-view. Simply put, the JSF runtime will invoke bookingBean.getTripId() and bookingBean.getSeats() and not tripBean.getTripId() and tripBean.getSeats().
While this might seem counter-intuitive, you ought to understand that the JSF specification treats this scenario quite differently. In most interactions performed by a user, the action URL of a component is generated when the user performs the action and not until then. The link tag on the other hand, requires preemptive computation of the URL, and hence the JSF runtime treats this quite differently as "pre-emptive navigation". When the URL is being constructed, the objects pertaining to the view parameters of the target view must be accessible, in order for the URL to be meaningful. The reason for the values being 0 in the URL, is that a new BookingBean instance is created during this evaluation and the default values are being used instead. You can quite obviously avoid this by using the TripBean instead (and most examples of includeViewParams demonstrate this), or you can set values during the construction of the BookingBean object.

Resources