39

From an action in my bean, I'm trying to redirect to another page expecting a view parameter. What is the recommended way to do this in JSF2?

E.g., say my source page is: http://localhost/page1.xhtml

it has a commandButton that calls an action:

<h:commandButton value="submit" action="#{myBean.submit}" />

where my bean looks like:

@ManagedBean
@RequestScoped
public class MyBean {

private int id;

public String submit() {
    //Does stuff
    id = setID();
    return "success";
}

And now, I want the 'submit' action's return to navigate to http://localhost/page2.xhtml?id=2

I've tried to do this with a view-param in my navigation case, but with odd results. The faces-config snippet looks like the following:

<navigation-rule>
    <from-view-id>/page1.xhtml</from-view-id>
    <navigation-case>
        <from-outcome>success</from-outcome>
        <to-view-id>/page2.xhtml</to-view-id>
        <redirect>
            <view-param>
                <name>id</name>
                <value>#{myBean.id}</value>
            </view-param>
        </redirect>
    </navigation-case>
</navigation-rule>

The weird behaviour being, even though myBean is set to request scoped, it only calls myBean.getId() the first time I load my application, and reuses that same value for all subsequent calls, producing incorrect view parameters for page2.

So I'm looking for either a better way to do this, or a reason/solution for why the view-param is not being requested from my bean each time.

Koray Tugay
  • 22,894
  • 45
  • 188
  • 319
dule
  • 17,798
  • 4
  • 39
  • 38

6 Answers6

59

The unintuitive thing about passing parameters in JSF is that you do not decide what to send (in the action), but rather what you wish to receive (in the target page).

When you do an action that ends with a redirect, the target page metadata is loaded and all required parameters are read and appended to the url as params.

Note that this is exactly the same mechanism as with any other JSF binding: you cannot read inputText's value from one place and have it write somewhere else. The value expression defined in viewParam is used both for reading (before the redirect) and for writing (after the redirect).

With your bean you just do:

@ManagedBean
@RequestScoped
public class MyBean {

private int id;

public String submit() {
    //Does stuff
    id = setID();
    return "success?faces-redirect=true&includeViewParams=true";
}

// setter and getter for id

If the receiving side has:

    <f:metadata>
        <f:viewParam name="id" value="#{myBean.id}" />
    </f:metadata>

It will do exactly what you want.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
fdreger
  • 12,264
  • 1
  • 36
  • 42
  • Did a small test with it, and I think it works how I wanted it to. Thanks! Though presumably, you meant "success?faces-redirect=true&includeViewParams=true"; and – dule Feb 01 '11 at 16:08
  • yes, sorry. I pasted both pieces of code from a working project to avoid typos, and here goes: the first one came from xml template (where ampersand has to be escaped) and is incomplete, and the othere one has different variable name. I'll edit it right away. – fdreger Feb 01 '11 at 21:49
  • @fdreger where would you put the metadata at when you are using an h:commandButton? – Kevin Jan 05 '12 at 22:55
  • @Kevin: As in the answer: metadata is always in the target page. It does not matter what you use on the source page (where the h:commandButton is). – fdreger Jan 06 '12 at 15:00
  • What do you do when the target page has a different backing bean than the sending page? In your example you have a sending bean name 'myBean' and a target bean 'myBeani'. But really I can't tell if myBeani is a different target bean or if the "i" in myBeani is a typo. And if I have several Ids in my sending bean, for example if I have a "personId" and an "itemId" in my sending bean, in the f:viewParam how are they realized? ??? – Bill Rosmus Nov 05 '12 at 05:20
  • 1
    @BillR: 1) it was a typo, sorry 2) there is no "sending bean" or "receiving bean" - there is a viewParam that is both bound to an expression and is able to receive data from query parameters. It works almost exactly like an ordinary inputText. If you want to create query parameters (change the value of the viewParam), just add some to your commandButton or link. 3) you can create many viewParams and they will all work, so yes, you can send and receive multiple parameters. – fdreger Nov 05 '12 at 12:00
  • The answer is not completely correct. If you define the `f:viewParam` with the same name in the source view and in the target view then the behavior before navigation is like this: try to get parameter value via target view binding; if null, try via source view binding. Found out with Mojarra 2.2.5. – Vsevolod Golovanov Apr 28 '14 at 10:04
18

Without a nicer solution, what I found to work is simply building my query string in the bean return:

public String submit() {
    // Do something
    return "/page2.xhtml?faces-redirect=true&id=" + id;
}

Not the most flexible of solutions, but seems to work how I want it to.

Also using this approach to clean up the process of building the query string: http://www.warski.org/blog/?p=185

dule
  • 17,798
  • 4
  • 39
  • 38
3

Check out these:

You're gonna need something like:

<h:link outcome="success">
  <f:param name="foo" value="bar"/>
</h:link>

...and...

<f:metadata>
  <f:viewParam name="foo" value="#{bean.foo}"/>
</f:metadata>

Judging from this page, something like this might be easier:

 <managed-bean>
   <managed-bean-name>blog</managed-bean-name>
   <managed-bean-class>com.acme.Blog</managed-bean-class>
   <managed-property>
      <property-name>entryId</property-name>
      <value>#{param['id']}</value>
   </managed-property>
 </managed-bean>
The Alchemist
  • 3,397
  • 21
  • 22
  • The problem with this is I'm not going direct through to the next page. My question relates to navigating from a bean action. Page 1 calls a bean action, bean action does stuff, returns "success", and now I want to navigate with the success but add a view param with the redirect. – dule Aug 31 '10 at 13:15
  • Sorry, I misunderstood your post. I think http://java.dzone.com/articles/bookmarkability-jsf-2 may get you what you want. The example is exactly what you are looking for, I think: `http://domain/blog/entry.jsf?id=9` – The Alchemist Aug 31 '10 at 13:56
  • Thanks for the responses, but I'm not sure if my question is still understood correctly. I'm unclear what example you're referring to in that article, because it does not discuss about navigating from an action. The closest thing in the article is passing a view-param along to the next page, but what I want is not passing the same view-param from one page to another, rather I want to pass in a new view-param into a page from an action navigation. Let me try to reword my original question to see if I can make it clearer. – dule Sep 01 '10 at 19:39
  • Hmm... you want to insert a *new* view-param? So something different from `whatever.jsf?id=2`? That's gonna take some interesting JSF work. – The Alchemist Sep 01 '10 at 23:14
2

You can do it using Primefaces like this :

<p:button 
      outcome="/page2.xhtml?faces-redirect=true&amp;id=#{myBean.id}">
</p:button>
ihebiheb
  • 3,673
  • 3
  • 46
  • 55
1

Just add the seen attribute to redirect tag as below:

<redirect include-view-params="true">
    <view-param>
        <name>id</name>
        <value>#{myBean.id}</value>
    </view-param>
</redirect>
Koray Tugay
  • 22,894
  • 45
  • 188
  • 319
-1

A solution without reference to a Bean:

<h:button value="login" 
        outcome="content/configuration.xhtml?i=1" />

In my project I needed this approach:

<h:commandButton value="login" 
        action="content/configuration.xhtml?faces-redirect=true&amp;i=1"  />
qecce
  • 71
  • 11
  • Bad choice for using a commandbutton here. Use a normal button: http://stackoverflow.com/questions/13070537/difference-between-hbutton-and-hcommandbutton (same is true for PrimeFaces p:button) – Kukeltje Aug 16 '16 at 12:06