17

I need to pass a parameter (POST) to a @managedBean, I used managed properties like this:

@ManagedProperty(value = "#{param.id}")
private int id;

And the scope of the Bean is ViewScope

I end up with this error:

Unable to create managed bean receipt. The following problems were found: - The scope of the object referenced by expression #{param.id}, request, is shorter than the referring managed beans scope of view

What can I do?

arjan take a look:

My page: Facelet Title

<form method="post" action="faces/index.xhtml">
  <input name="id" value="4" />
  <input type="submit" value="submit" />
</form>

<h:form>
  <h:commandLink value="click" action="index">
    <f:param id="id" name="id" value="20"/>
  </h:commandLink>
</h:form>

ehsun7b
  • 4,796
  • 14
  • 59
  • 98

2 Answers2

39

Two ways:

  1. Make the bean request scoped and inject the view scoped one as another @ManagedProperty.

    @ManagedBean
    @RequestScoped
    public class RequestBean {
    
        @ManagedProperty(value="#{param.id}")
        private Integer id;
    
        @ManagedProperty(value="#{viewBean}")
        private ViewBean viewBean;
    }
    

    The view scoped bean is available during @PostConstruct and action methods of request scoped bean. You only need to keep in mind that the id can get lost when you do a postback to the same view without the parameter.

  2. Or, grab it manually from the request parameter map during bean's initialization.

    @ManagedBean
    @ViewScoped
    public class ViewBean {
    
        private Integer id;
    
        @PostConstruct
        public void init() {
            id = Integer.valueOf(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id"));       
        }
    }
    

    This way the initial id is available during the entire view scope.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    Thanks, is the first solution (injecting the viewScoped bean in the requestScoped one) a good and popular practice??? – ehsun7b Jan 02 '11 at 18:35
  • My app is being served by GlassFish and I can use CDI beans too, but I decided to do some project using the JSF beans cause I'm very new in JSF, is it a good idea? – ehsun7b Jan 02 '11 at 18:38
  • 2
    Depends on the purpose of the `id`. Do you want to "refresh" it on every request? Go for way 1. Or do you want to use the initial value during the entire view scope? Go for way 2. – BalusC Jan 02 '11 at 18:48
  • Thanks, very complete description. – ehsun7b Jan 02 '11 at 19:42
  • Would you please read the other answer which was post on my question and give me your idea? – ehsun7b Jan 02 '11 at 20:02
  • That's a third way (which I didn't think about) which does effectively the same as the first way, but then without a request scoped bean. It's also more declarative than the second way. – BalusC Jan 02 '11 at 21:28
  • Hi, somehow I cannot parse Get parameter in the bean contructor, I need to add an @PostConstruct,anybody know why? – Heetola Aug 13 '12 at 09:50
  • @Eildosa: you're talking about the `@ManagedProperty`? That's pretty logical. How would *you* call the setter method on an instance before its constructor? – BalusC Aug 13 '12 at 09:55
  • nope, talking about id = Integer.valueOf(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id")); this will only work in a @PostConstruct – Heetola Aug 13 '12 at 09:59
  • @Eildosa: this is not normal. Your concrete problem is caused elsewhere. – BalusC Aug 13 '12 at 09:59
  • @BalusC thanks for you asnwers, is it normal that you didn't add setters for the managed properties, or its just because its not the purpose of the OP's question ? – Tarik Feb 05 '15 at 02:27
  • 1
    @Tarik: I always omit obviousness from code snippets to make the answer better focused. Given the way how the question is formulated, the OP is very well aware of the need for getters/setters. If the OP weren't aware, I'd rather have added a comment like `// +getter+setter` instead of adding a whole bunch of obviousness. Just let your IDE autogenerate getters/setters and hide them away all in the bottom of the class. – BalusC Feb 05 '15 at 08:29
  • @Tarik Or you can you Lombok for this purpose. – alexander Dec 04 '15 at 14:20
6

As an alternative to grabbing the parameters directly from the request in your bean, you can use view parameters.

These need to be declared on the Facelet where you use your managed bean as follows:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
>
    <h:body>

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

    <!-- Rest of Facelet here -->   

    </h:body>
</html>

If you now request this page, the setter of the backing bean will be called with the request value provided for the id parameter. This works for both GET and (non-faces) POST requests.

The advantage is that you can use the standard JSF converters and validators here. Of course if your managed bean is not tied to a particular view then this solution is less ideal.

A small peculiar thing to watch out for is that when doing a normal faces postback after the initial request that provided the view parameter, the setter in your bean will be called again, even if the bean is in view scope and no new value is explicitly provided.

To test that this works, I've used the following managed bean:

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;

@ManagedBean
@ViewScoped
public class MyBean {

    Long id;

    @PostConstruct
    public void test() {
        System.out.println("post construct called");
    }

    public void actionMethod(ActionEvent event) {       
        System.out.println("action called");        
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

}

And the following 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"
>
    <h:body>

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

        <h:outputText value="#{myBean.id}"/>

        <h:form>
            <h:commandButton value="test" actionListener="#{myBean.actionMethod}"/>
        </h:form>

        <form method="post">
            <input name="id" value="4" />
            <input type="submit" value="submit" />
        </form>

    </h:body>
</html>

Enter a number in the input field and click the submit button. If the number is printed back on the screen the test has succeeded. Note that the second form is a regular form and does not post any JSF state along. I tested this on JBoss AS 6 and it works. Proving the id parameter as a GET parameter also works.

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
  • As far as I find in the books and tutorials they only work for Get parameters, are you sure that they can be used with Post parameters????... – ehsun7b Jan 02 '11 at 20:01
  • 1
    Uhm, although I'm no authoritative source on this I'm pretty sure I have used this before with POST parameters. I'll check it again and update my answer. – Arjan Tijms Jan 02 '11 at 20:03
  • I tested again and updated my answer. It's just two files so it should be easy for you to copy. – Arjan Tijms Jan 02 '11 at 20:18
  • I also took a look in the Mojarra source code to see how those parameters are ultimately resolved. In Mojarra 2.03 it's in UIViewParameter line 217: String paramValue = context.getExternalContext().getRequestParameterMap().get(getName()); since the request parameter map always contains parameters from both POST and GET requests, this thus automatically explains that it should work. In the JSF spec there is however no explicit statement that BOTH POST and GET should be supported. The spec simply doesn't seem to be explicit about this. – Arjan Tijms Jan 02 '11 at 20:44
  • Thanks for the time you have spent for me. Good luck :). Java programmers are as good as the programming language is. – ehsun7b Jan 03 '11 at 05:37
  • I tested it, as you say, it works fine with but it does not work with tag, I updated my question, can you take a look at it? – ehsun7b Jan 03 '11 at 05:59
  • The things you tried so far and what you'd been given answers for is mainly for capturing request parameters from an external request (a non-faces request). With commandlink you have some other options as described here: http://stackoverflow.com/questions/3599948/jsf2-action-parameter or in several other articles and answers by BalusC. – Arjan Tijms Jan 03 '11 at 10:02
  • @ArjanTijms arjam I used the ManagedProperty to get param.id. Now I use this in one method to retrieve request associted with this Id, but next time I try to use it, it is null i.e. private String id. any idea why it cleared the value? – sys_debug Dec 19 '11 at 06:46