2

I have a backing bean as following:

@Named
@RequestScoped
public class ClientNewBackingBean {

    @Inject
    private ClientFacade facade;
    private Client client;

The Client class has a List<Child> childrenList attribute, among others. I'm able to create a new Client when setting the childrenList with new ArrayList().

In the view, I have a input text field and an Add Child button. The button has the attribute actionListener=#{clientNewBackingBean.addChild()} implemented as:

public void addChild() {

    if(client.getChildrenList() == null) {
        client.getChildrenList(new ArrayList());
    }

    Child c = new Child("John Doe");

    client.getChildrenList().add(c);
}

Everytime the Add Child button is clicked, the bean is recreated and the view only shows one John Doe child (due to it being Request scoped, I believe). Is there another way to solve this besides changing the bean scope to Session?

Tiny
  • 27,221
  • 105
  • 339
  • 599
kauedg
  • 827
  • 8
  • 20

2 Answers2

9

If you were using standard JSF bean management annotation @ManagedBean, you could have solved it by just placing the bean in the view scope by @ViewScoped.

@ManagedBean
@ViewScoped
public class ClientNewBackingBean implements Serializable {

    @EJB
    private ClientFacade facade;

    // ...

In CDI, the @ViewScoped however doesn't exist, the closest alternative is @ConversationScoped. You only have to start and stop it yourself.

@Named
@ConversationScoped
public class ClientNewBackingBean implements Serializable {

    @Inject
    private Conversation conversation;

    // ...

    @PostConstruct
    public void init() {
        conversation.begin();
    }

    public String submitAndNavigate() {
        // ...

        conversation.end();
        return "someOtherPage?faces-redirect=true";
    }

}

You can also use the CDI extension MyFaces CODI which will transparently bridge the JSF @ViewScoped annotation to work properly together with @Named:

@Named
@ViewScoped
public class ClientNewBackingBean implements Serializable {

    @Inject
    private ClientFacade facade;

    // ...

A CODI alternative is to use @ViewAccessScoped which lives as long as the subsequent requests reference the very same managed bean, irrespective of the physical view file used.

@Named
@ViewAccessScoped
public class ClientNewBackingBean implements Serializable {

    @Inject
    private ClientFacade facade;

    // ...

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • the lack of `@ViewScoped` in CDI was exactly the problem. I will use the `@ConversationScoped`. Your solution is clean and precise as always. Thanks. – kauedg Jan 04 '13 at 16:03
  • I set the code as in the given example but when I click `Add Child` it still calls the `@PostConstruct` annotated method again. What am I missing? – kauedg Jan 04 '13 at 16:49
  • 1
    Changed to the MyFaces CODI approach and it works. I'll stick to it. – kauedg Jan 04 '13 at 17:06
  • 1
    MyFaces CODI bridges `@ViewScoped` to CDI. So you don't have to use a different annotation. `@ViewAccessScoped` is a completely different Scope with a different behaviour. CODI Scopes are in 99% the better choice! – Dar Whi Jan 05 '13 at 01:51
  • @DarWhi: right, thank you for the correction. I updated the answer. – BalusC Jan 05 '13 at 02:01
  • @DarWhi how do I use this bridging? Just add the MyFaces CODI dependency (it's a Maven project) and import `javax.faces.bean.ViewScoped`, or I must import a MyFaces CODI specific class? – kauedg Jan 07 '13 at 12:07
  • 2
    Just the JSF standard annotation, yes. – BalusC Jan 07 '13 at 12:24
  • I have added the MyFaces CODI dependencies and annotated the bean `@ViewScoped`. Added `@PostConstruct` and `@PreDestroy` annotated methods to the bean. Every time the bean is requested by the view (e.g. commandLink that calls an actionListener method), it behaves like a `@RequestScoped` bean (construct, @PostConstruct and @PreDestroy methods are executed). Any idea whats the problem? – kauedg Jan 11 '13 at 16:07
  • @DarWhi may better be able to answer this particular issue (he's one of the MyFaces CODI guys, so I assumed his statement to be true). I've myself not used this construct before. – BalusC Jan 11 '13 at 16:13
  • Good find. Thank you for sharing this. – BalusC Jan 11 '13 at 16:29
  • Thanks @BalusC. I deleted the previous answer (saying that it was related to the specific implementation) because it's a little more complex than I thought. I'm going to open a new question with the specific issue. – kauedg Jan 11 '13 at 16:37
  • The problem: to go to the "new client" view, I was using a ``. The bean was getting created, it's `prepareView()` method got called, destroyed and then created again. – kauedg Jan 11 '13 at 16:57
  • The solution: The `prepareCreate()` call caused the bean to be created and initiated, then the `action` part kicked in and the navigation destroyed the bean. Any method call from the view would cause the bean to be created again. So I removed the `actionListener` attribute and that's it. Also, I added the right maven pom for MyFaces CODI (myfaces-extcdi-assembly-jsf20 in case some one needs). – kauedg Jan 11 '13 at 16:57
  • 1
    #1 I'm not one of the CODI devs (only a "poweruser") #2 That's the correct behaviour of `@ViewScoped` (defined in the JSF spec.) #3 IMHO `@ViewScoped` is only useful for some very rare cases. `@ViewAccessScope` does what you expect – Dar Whi Jan 12 '13 at 13:59
  • @DarWhi I'm learning JSF by myself, could you please provide some content on the difference between CODI's `@ViewAccessScope` and `@ViewScoped`? Up there you said that `@ViewAccessScope` "is a completely different Scope with a different behaviour" and got me confused saying that `@ViewScoped` is only for rare cases. – kauedg Jan 14 '13 at 13:23
  • 1
    `@ViewScoped` is specified by the JSF spec. and is bound to one view. You can't use the stored values on a different page -> if you have a page and have lots of AJAX requests to the same page and you don't need the values on the next page, you should use it. `@ViewAccessScoped` is a custom CDI scope invented by CODI. Your beans exist for the _next_ logical (not HTTP - could be 2 HTTP requests if you trigger a nav. with a redirect) request as long as they get touched during a logical request. If needed you can also destroy them earlier (manually). – Dar Whi Jan 16 '13 at 18:29
0

If you are using JSF 2, you should use ViewScoped bean.

partlov
  • 13,789
  • 6
  • 63
  • 82