2

I wanted to add sorting to a PrimeFaces 3.3 dataTable and created the following ViewScoped bean which stores the list so it is not fetched all the time from the EJB:

@Named
@ViewScoped
public class CustomerList implements Serializable {

    private static final long serialVersionUID = 6168621124401208753L;

    @EJB 
    CustomerEJB customerBean;

    List<Customer> allCustomers = null;

    public void loadCustomerList() {
        allCustomers = customerBean.findAll();
    }

    public List<Customer> getList() {
        if (allCustomers == null) {
            loadCustomerList();
        }
        return allCustomers;
    }

}

and this is the view using the bean:

<ui:composition template="/WEB-INF/templates/template.xhtml">

  <ui:define name="content">

        <h:form id="customerList">

          <p:dataTable id="customer" var="customer"
            value="#{customerList.list}" sortBy="#{customer.id}"
            paginator="true" rows="10" paginatorAlwaysVisible="false"
            paginatorPosition="top">
            <p:column sortBy="#{customer.id}">
              <f:facet name="header">
                <h:outputText value="#{msg.customerIdLabel}" />
              </f:facet>
              <h:outputText value="#{customer.id}" />
            </p:column>
            <p:column sortBy="#{customer.lastName}">
              <f:facet name="header">
                <h:outputText value="#{msg.customerLastNameLabel}" />
              </f:facet>
              <h:outputText value="#{customer.lastName}" />
            </p:column>

The issue is that i can click the column headers for sorting, but the table remains unsorted, even the initial sorting is not working. When u set a breakpoint in the getList() method i can see that the list is fetched several times from the EJB during when a request is processed. Shouldn't the bean be stored as long as the view is active by ViewScope?

Joysn
  • 987
  • 1
  • 16
  • 30

3 Answers3

2

First of all, not related directly to your problem but: You should never place your business methods into component getters (even with that null check I think that this is a bad practice). Use the @PostConstruct annotation instead:

@PostConstruct
public void loadCustomerList() {
    allCustomers = customerBean.findAll();
}

public List<Customer> getList() {
    return allCustomers;
}

loadCustomerList will be called every time when your ViewScoped bean is constructed. Also, check your imports for scope annotations:

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

Finally you class should look like:

@ViewScoped
@ManagedBean
public class CustomerList implements Serializable {
    ...
}
Akos K
  • 7,071
  • 3
  • 33
  • 46
  • thank u for the hint with the PostConstruct. Changed it and works nicely :) regarding the ViewScope and ManagedBean: unfortunately i do not know exactly when to use Named and when to use ManagedBean. when i started the project i used only ManagedBean, but then CDI gots interesting for me i changed to Named, and ConversationScoped when it seemed appropriate. Now i use Named and (CDI) ConversationScoped for this usecase and it works. So, i will stick to it. I dont want mix ManagedBean and Named, although i dont know when to use what. Are there best or bad practices? – Joysn Sep 02 '12 at 08:30
  • 2
    In a lot of situations `@PostConstruct` is indeed better, but lazy loading is not necessarily a bad practice. If the getter is only called occasionally, than loading it upfront lets you pay the cost of loading the data for all those occasions where the getter isn't called. – Arjan Tijms Sep 02 '12 at 08:54
  • 1
    @Joysn not sure is there any good/bad practice in choosing Faces Managed Beans over CDI managed beans in general. In my JSF application (PF and PF-Extensions app) I'm going with Faces Managed Beans because of `@ViewScoped`. Every PF/PF-E component worked as expected with these scopes so I stick with them. If you want to learn more about Faces Managed Beans I recommend the [Communication in JSF 2.0](http://balusc.blogspot.com/2011/09/communication-in-jsf-20.html) article by BalusC. – Akos K Sep 02 '12 at 09:16
  • 1
    To understand the difference see here : http://stackoverflow.com/questions/11986847/java-ee-6-javax-annotation-managedbean-vs-javax-inject-named-vs-javax-faces – Mehdi Sep 02 '12 at 09:38
  • @Heidarzadeh thanks for the pointer. With that insights i needed to place another question :) http://stackoverflow.com/questions/12235464/jee6-controlling-startup-of-managed-beans-with-dependencies-cdi-ejb – Joysn Sep 02 '12 at 11:34
  • @akoskm Unfortunately i need something like conversation scoped because my use case is like this: list view (orders) -(add/edit)-> detail1 view (order) -(add/edit)-> detail2 view (customer) – Joysn Sep 02 '12 at 11:35
  • @akoskm u are completely right. the sorting only works with ManagedBean and ViewScope as it should. as my managed bean provides data which is presented in two different views thats the only approach which worked. thanks a lot! :) – Joysn Sep 02 '12 at 19:31
  • but there is still some wierdness :) if i place a `h:selectOneMenu` on one view, e.g. to select which rows should be displayed in the dataTable, even without updating the table using ajax, the sorting breaks... without the drop down it works... :) JSF is so incredible fragile... – Joysn Sep 02 '12 at 20:46
1

@ViewScoped is not supported in CDI so If you need to use @ViewScoped in CDI you should

  • use seam-faces or MyFaces CODI module. just add one of them to your classpath and @ViewScoped will work in CDI. MyFaces CODI has an even more solid support of @ViewScoped
  • use MyFaces CODI's @ViewAccessScoped, it is an extension written on top of CDI by Apache, just download it and use the @ViewAccessScoped annotation instead of @ViewScoped.
  • Use CDI @ConversationScoped and make it long running. see here for more info.

Unfortunately the seam3 solution has a memory leak problem so don't use seam3 for this special problem better solutions is CODIs @ViewAccessScoped.

See : Memory leak with ViewScoped bean?

Community
  • 1
  • 1
Mehdi
  • 4,396
  • 4
  • 29
  • 30
  • - regarding the first possibility, simply adding MyFaces CODI to the classpath: i added myfaces-extcdi-core-api and myfaces-extcdi-core-impl to my pom, but nothing changed. The @ViewScoped bean is still created on every 'sort click'. – Joysn Sep 01 '12 at 22:02
  • 1
    - with @ViewAccessScoped i get the following error message when i open the bean referencing view: WELD-001303 No active contexts for scope type org.apache.myfaces.extensions.cdi.core.api.scope.conversation.ViewAccessScoped – Joysn Sep 01 '12 at 22:12
  • - @ConversionScoped: i had the bean annotated with this scope, but as i navigate to that page using a usual url i could start the conversation, but i dont know when to end the conversation. As the user might click on some other page url on the menu. And, from the datatable where i present entities a user can open an edit page where a new conversion for the edit session is created. and that conversation is ended using save or cancel buttons. – Joysn Sep 01 '12 at 22:15
  • oh, sorry. i forgot to put the myfaces-extcdi-jsf20-module-* dependencies into the pom. Now its working with the @ViewAccessScope :) – Joysn Sep 01 '12 at 22:55
  • unfortunately other things broke now :( another view which uses a ViewScoped bean is not able to set an entry from a dataTable to a commandButtons action method anymore: `#{taskManager.claimTask(v_task)}` the v_task in the method is NULL. Changind to ViewAccessMethod did not help... Are there more things to consider when adding MyFaces CODI? – Joysn Sep 01 '12 at 23:11
  • 1
    When working with CDI generally do not use `@ViewScoped`, if you want to use CODI, `@ViewAccessScoped` should help. Feel free to ask another questions for your other problems I will be glad to help you ;) – Mehdi Sep 02 '12 at 04:11
  • one more question regarding sorting :) when i sortBy a specific column, and then move to a detail view for editing purpose and then come back to the list view, can i get back the sorting i had before leaving the list view? i think from the users experience it would be nice when he return back to the list and see the list sorted the same way as he left it. And it would be nice when the list contains the new entity which eventually was added to the list using an Add/detail view action... – Joysn Sep 02 '12 at 08:57
  • In that case you should store sorted column name in a scope that is longer than `@ViewAccessScoped` and use it to sort the table. which can be `@SessionScoped` or `@ConversationScoped`. a long running `@ConversationScoped`can be a good solution. – Mehdi Sep 02 '12 at 09:37
  • hm... i just tried to get the sorted column name out of the dataTable... but i dont know how to do it. i thought i need the following at the dataTable `sortBy="#{customerList.getSortBy(customer.id)}"` which should get the remembered sort column with a default at initial view rendering, but when i change the sort column i can not catch the new sorting column name... – Joysn Sep 02 '12 at 12:43
  • i switched back to ConversationScoped as it works at two different views and i am not sure yet if MyFaces CODI introduces other issues as my login page is displayed twice when a session times out now... – Joysn Sep 02 '12 at 14:40
  • 1
    Don't forget the JSF module provided by MyFaces CODI. The Core is independent of JSF. Or use one of the bundles (for JSF 1.2 or 2.x). – Dar Whi Sep 03 '12 at 15:02
0

you need to use this Annotation Bean combination:

for @ManagedBean u need to use @ViewScoped

for @Named (CDI) u need to use @ConversationScoped

zho
  • 643
  • 1
  • 12
  • 27