3

I have this SearchBean:

@ManagedBean(name = "searchBean")
@RequestScoped
public class SearchBean implements Serializable
{
    private String input = null;

    // getter methods
    public String getInput() {
        return input;
    }

    // setter method
    public void setInput(String input) {
        this.input = input;
    }

    public String Submit() {
        return null;
    }    
}

Can I inject it into another bean using @ManagedProperty. For example:

@ManagedBean(name = "bookBean")
@RequestScoped
public class BookBean implements Serializable
{
    @ManagedProperty(value = "#{searchBean}")
    private SearchBean searchBean;  

    @PostConstruct
    public void init()
    {   
       System.out.println("Value: " + searchBean.getInput());
    }

    public SearchBean getSearchBean() {
       return searchBean;
    }

    public void setSearchBean(SearchBean searchBean) {
       this.searchBean = searchBean;
    }   
}

And the Facelet (search.xhtml):

<h:form id="formSearch">
   <h:commandButton value="Search" action="#{searchBean.Submit}" />
</h:form>

UPDATE: I have search.xhtml inserted into book.xhtml via a ui:insert component as follow:

<h:form id="formBooks">
   <ui:insert name="search">
      <ui:include src="/templates/common/search.xhtml"/>
   </ui:insert> 
</h:form>

The searchBean.getInput() method above should return a value as a result of a form's submission. Is the above method of injection possible?

ChuongPham
  • 4,761
  • 8
  • 43
  • 53
  • @user: I deleted my answer because that did only answer your *initial* question if the managed property injection would work (which is obviously "yes") but McDowell did a better job of answering your **real** problem (that the property is null at the point `@PostConstruct` is invoked. – BalusC Apr 13 '11 at 13:14
  • @BalusC: I thought I had a problem with my browser when your answer disappeared after a refresh. Yes, sorry about that. I'm still trying to figure out the specs by doing practical examples to learn but sometimes I get stuck and I don't know how to move forward. Thanks for your answer, though. – ChuongPham Apr 13 '11 at 13:18

1 Answers1

9

I assume that SearchBean.input will be bound to an input field:

public class SearchBean implements Serializable {
    private String input = null;

Something like this:

<h:inputText value="#{searchBean.input}" />

If so, then this will be null:

@PostConstruct
public void init()
{   
   System.out.println("Value: " + searchBean.getInput());
}

But, assuming a value has been set, it will not be null when this method is invoked:

public String Submit() {
    return null;
}

Image from Richard Hightower's JSF for nonbelievers: The JSF application lifecycle

Image from Richard Hightower's JSF for nonbelievers: The JSF application lifecycle.

The reason is due to how the JSF lifecycle works:

  1. When #{searchBean...} is first resolved and found not to exist:
    • The bean is instantiated
    • Any dependency injections are performed (there aren't any in this case)
    • @PostConstruct method is invoked
    • The bean is placed into scope
  2. Assuming the Apply Request Values and Validations phases succeed, SearchBean.setInput(String) is invoked in the Update Model Values phase
  3. SearchBean.Submit() is invoked in the Invoke Application phase

This process is defined in the JSF specification.


Now, if SearchBean.input were injected directly from the parameter map, it would not be null during @PostConstruct:

@ManagedProperty(value = "#{param.someParamName}")
private String input;

However, there aren't any real advantages to this - you're skipping any input validation and you can't use SearchBean.input as a field binding because it will be overwritten in the Update Model Values phase.

The SearchBean.Submit() method is where your application logic for performing the search should go.

McDowell
  • 107,573
  • 31
  • 204
  • 267
  • @McDowell: That part of the spec is still a bit fuzzy to me so I thought I do a practical example to see how it works. You're right: I have the `searchBean#input` bound to `h:inputText` and by your explanation the value returned will be null - which explains why I got null returned when the form is submitted. Could you explain - perhaps with some codes example - how to set the value in the `SearchBean.Submit()` method. I don't understand this part. – ChuongPham Apr 13 '11 at 13:12
  • If you bind the input field to `SearchBean`, inject it as managed property in `BookBean` and bind the command button to `BookBean`, then the searchbean and the input value will be there. The postconstruct is invoked directly after bean's construction and dependency injection, but far before the input values are been set. – BalusC Apr 13 '11 at 13:20
  • @user463053 - the input control will set `SearchBean.input` during Update Model Values; you can just reference `this.input` in the `Submit()` method when it is called during Invoke Application. – McDowell Apr 13 '11 at 13:21
  • @BalusC: You mean something like this `@ManagedProperty(value = "#{param.input}") private String input` in `BookBean` class, and then just use `input` in some database method? – ChuongPham Apr 13 '11 at 13:33
  • That's *another* way. What I meant was to put the action method `submit()` in `BookBean`, let the command button point to it and finally do the database job in that action method. – BalusC Apr 13 '11 at 13:34
  • @McDowell: This would mean my database logic from `BookBean` will need to be moved to `SearchBean` in order to use `this.input`. Or, have I misunderstood your comment entirely? -:) – ChuongPham Apr 13 '11 at 13:36
  • @user463053 - it might have to - it depends on the design you opt for - you could 1) lazily fetch the data in a `BookBean` getter or 2) inject the `BookBean` into `SearchBean` instead of the other way round and populate it in `Submit()` 3) dispense with input bindings/POST and use a GET with the parameter on the URL (a more significant design change). There isn't a single right way to do this. – McDowell Apr 13 '11 at 13:43
  • @BalusC: I gather then I have to replicate the `getInput` and `setInput` methods together with the `Submit` method in `BookBean` too. – ChuongPham Apr 13 '11 at 13:50
  • No, you can keep them in `SearchBean`. You just get the input by `searchBean.getInput()` in action method of `BookBean`. Exactly as you would do in `@PostConstruct`, but then at the right place/moment instead. – BalusC Apr 13 '11 at 13:51
  • @McDowell: Currently the Search form is used as a subform by three other forms (because the search requirements are the same across these forms). Your suggestion is good but I will have to convince my boss for the changes. I don't know what he'll say. He already gave me the fits when I switched from OpenJPA to EclipseLink. -:) – ChuongPham Apr 13 '11 at 13:55
  • @BalusC: That works! You're the man. Can you please post your last comment as a separate answer so I can tick your answer. – ChuongPham Apr 13 '11 at 13:58
  • @user463053 - I believe @BalusC means (in effect) delete the `@PostConstruct` attribute and just bind the `commandButton` to `#{bookBean.init}` - I agree that this would work also. You might need to have `init()` return a _String_ to conform to the letter of the spec. – McDowell Apr 13 '11 at 13:59
  • @McDowell: I will also tick your original response as having answered my question (the JSF Lifecycle diagram is fantastic!). Thanks you for helping me, too. Much appreciated. Java is certainly more interesting than .Net (I'm an ex-Microsoft fanboy!) -:)... – ChuongPham Apr 13 '11 at 14:02