2

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>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
StefK
  • 604
  • 6
  • 18

2 Answers2

2

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>
Lii
  • 11,553
  • 8
  • 64
  • 88
fdreger
  • 12,264
  • 1
  • 36
  • 42
  • I found that out as a workaround. But you put the values of tripid and seats in the tripBean when loading trip.xhtml. You can pass through viewParams with includeViewParams. Then why can't I pass the values to another page with a link that tries to catch those parameters with another bean? – StefK Aug 25 '11 at 10:49
  • Using f:param instead of the includeViewParams attribute – StefK Aug 25 '11 at 11:11
  • @Trinorae: no, it is not a workaround, because f:param and f:viewParam (UIViewParameter) are two different things. The functionality you need is covered by f:param. What includeViewParams does IS NOT WHAT YOU WANT OR NEED. You only want to use it because you like the name better, which is silly. – fdreger Aug 25 '11 at 11:33
1

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.

Vineet Reynolds
  • 76,006
  • 17
  • 150
  • 174
  • The trip.xhtml page DOES get the parameters tripid and seats. If I understand well, the parameters are read in the Apply Request Values phase. These are put in the TripBean in the Update Model Values and the link is generated in the render-response phase. So when I give those two parameters to the trip.xhtml, the booking-link should be generated correct, right? But this only seems to happen when the booking.xhtml also uses the tripBean to catch the parameters and not when I try this with another (bookingBean) bean! – StefK Aug 25 '11 at 10:44
  • @Trinorae: you misunderstand the f:paramView. The behavior you describe is EXACTLY how it should work, it is NOT a fault or problem, and therefore it DOES NOT require a workaround. f:paramView IS NOT meant to move values between two beans. It is DESIGNED to keep the source the same as the target. This behavior is CONSISTENT with how other JSF components work. The elegant way to produce parameters from arbitrary source is the f:param... Basically, you are trying to hakishly squeeze a well designed piece of JSF into what you misperceive as "nice", and at the same time refuse to learn. Phew. – fdreger Aug 25 '11 at 11:08
  • I guess I have misunderstood the use of the viewParams then! Thanks for enlighting me. – StefK Aug 25 '11 at 11:20
  • @Trinorae: sorry for the caps. I have a bad day and I sort of can't help taking it out on everyone around. At least I tried to do it in a helpful manner :-) – fdreger Aug 25 '11 at 11:36