8 Replies Latest reply on Apr 1, 2009 10:08 AM by shino

    problem with datatable, sorting and datascroller

    shino

      Hi all,

      in my application, I have a datatable which spans over several pages. All I actually want to do is to sort that table by one or more columns.
      The sorting does work, however, from the moment that I click on the sorting icon, the datascroller doesn't work anymore.

      I have tried a lot of different configurations, but I will spare you the details since it didn't work anyway.

      Here is a short description and the code:
      The dataTable gets it's values from a model bean (addressModel) which - as of now - just provides manually filled arrayList's for column headers and cell values. This bean lives in the session.

      The dataTable component is bound to a htmldatatable property of a backingbean (addressTableBean) which also lives in the session (I figured it had to, since sorting doesn't seem to affect the actual model so I suppose this information is stored in the htmldatatable object...right?)

      Here is my view.xhtml:

      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
       xmlns:ui="http://java.sun.com/jsf/facelets"
       xmlns:h="http://java.sun.com/jsf/html"
       xmlns:f="http://java.sun.com/jsf/core"
       xmlns:a4j="http://richfaces.org/a4j"
       xmlns:rich="http://richfaces.org/rich">
      
       <rich:panel header="#{msgs.addressOverviewTitle}"
       style="height : auto; width : auto;">
       <rich:spacer height="6"></rich:spacer>
       <h:form>
       <rich:dataTable
       value="#{AddressesModel.addressesTableData}"
       id="addressOverviewTable"
       binding="#{AddressesTableBean.addressesTable}"
       var="address"
       columns="3"
       headerClass="addressesTableHeader"
       rowClasses="oddRow,evenRow"
       rows="10">
       <rich:column>
       <f:facet name="header">
       <h:outputText value="#{AddressesModel.addressesTableLabels[0]}" />
       </f:facet>
       <h:outputText value="#{address.reference}" />
       </rich:column>
       <rich:column sortBy="#{address.name}">
       <f:facet name="header">
       <h:outputText value="#{AddressesModel.addressesTableLabels[1]}" />
       </f:facet>
       <h:outputText value="#{address.name}" />
       </rich:column>
       <rich:column>
       <f:facet name="header">
       <h:outputText value="#{AddressesModel.addressesTableLabels[7]}" />
       </f:facet>
       <h:outputText value="#{address.country}" />
       </rich:column>
      
       <f:facet name="footer">
       <rich:datascroller for="addressOverviewTable" page="#{AddressesTableBean.dataScrollerPage}" binding="#{AddressesTableBean.addressesTableScroller}" maxPages="10" />
       </f:facet>
       </rich:dataTable>
       </h:form>
       <a4j:log popup="false" />
       </rich:panel>
      </ui:composition>

      binding the datascroller to a bean property was something I tried out of desperation.. if it's unnecessary or simply wrong, please let me know.

      faces-config.xml:
      <?xml version="1.0"?>
      <faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xi="http://www.w3.org/2001/XInclude"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
      
       <navigation-rule>
       <from-view-id>/*</from-view-id>
       <navigation-case>
       <from-outcome>edit</from-outcome>
       <to-view-id>
       /edit.jsf?javax.portlet.faces.PortletMode=edit
       </to-view-id>
       </navigation-case>
       <navigation-case>
       <from-outcome>help</from-outcome>
       <to-view-id>
       /help.jsf?javax.portlet.faces.PortletMode=help
       </to-view-id>
       </navigation-case>
       <navigation-case>
       <from-outcome>back</from-outcome>
       <to-view-id>
       /view.jsf?javax.portlet.faces.PortletMode=view
       </to-view-id>
       </navigation-case>
       </navigation-rule>
       <render-kit>
       <renderer>
       <description>override the viewroot</description>
       <component-family>javax.faces.ViewRoot</component-family>
       <renderer-type>
       org.jboss.portletbridge.ViewRoot
       </renderer-type>
       <renderer-class>
       org.jboss.portletbridge.renderkit.portlet.PortletAjaxViewRootRenderer
       </renderer-class>
       </renderer>
       </render-kit>
       <application>
       <view-handler>
       org.jboss.portletbridge.application.PortletViewHandler
       </view-handler>
       <state-manager>
       org.jboss.portletbridge.application.PortletStateManager
       </state-manager>
       <!-- <view-handler>com.sun.facelets.FaceletPortletViewHandler</view-handler>
       -->
       <!-- PortletFaces resolvers that extend the JSF Expression Language (EL) -->
       <variable-resolver>
       com.liferay.portletfaces.resolver.PortletVariableResolver
       </variable-resolver>
       <property-resolver>
       com.liferay.portletfaces.resolver.PortletPropertyResolver
       </property-resolver>
       <resource-bundle>
       <base-name>messages</base-name>
       <var>msgs</var>
       </resource-bundle>
       <locale-config>
       <default-locale>de</default-locale>
       <supported-locale>de</supported-locale>
       <supported-locale>en</supported-locale>
       <supported-locale>en_US</supported-locale>
       <supported-locale>de_DE</supported-locale>
       </locale-config>
       </application>
       <managed-bean>
       <managed-bean-name>AddressesTableBean</managed-bean-name>
       <managed-bean-class>
       de.test.addressbook.backingbean.AddressesTableBean
       </managed-bean-class>
       <managed-bean-scope>session</managed-bean-scope>
       </managed-bean>
       <managed-bean>
       <managed-bean-name>AddressesModel</managed-bean-name>
       <managed-bean-class>
       de.test.addressbook.modelbean.AddressesModel
       </managed-bean-class>
       <managed-bean-scope>session</managed-bean-scope>
       <managed-property>
       <property-name>addressesTableLabels</property-name>
       <list-entries>
       <value-class>java.lang.String</value-class>
       <value>#{msgs.referenceColumnHeader}</value>
       <value>#{msgs.nameColumnHeader}</value>
       <value>#{msgs.nameExtColumnHeader}</value>
       <value>#{msgs.streetColumnHeader}</value>
       <value>#{msgs.houseNoColumnHeader}</value>
       <value>#{msgs.PostalCodeColumnHeader}</value>
       <value>#{msgs.cityColumnHeader}</value>
       <value>#{msgs.countryColumnHeader}</value>
       <value>#{msgs.nonPublicColumnHeader}</value>
       <value>#{msgs.adColumnHeader}</value>
       </list-entries>
       </managed-property>
       </managed-bean>
       <lifecycle>
       <phase-listener>de.test.utility.PhaseTracker</phase-listener>
       </lifecycle>
      
      </faces-config>
      


      addressesTableBean:
      public class AddressesTableBean implements Serializable{
      
      private static final long serialVersionUID = 1291741947809283807L;
      private HtmlDataTable addressesTable;
      private HtmlDatascroller addressesTableScroller;
      private int dataScrollerPage;
      
      public int getDataScrollerPage() {
       return dataScrollerPage;
      }
      
      public void setDataScrollerPage(int dataScrollerPage) {
       this.dataScrollerPage = dataScrollerPage;
      }
      
      public HtmlDatascroller getAddressesTableScroller() {
       return addressesTableScroller;
      }
      
      public void setAddressesTableScroller(HtmlDatascroller addressesTableScroller) {
       this.addressesTableScroller = addressesTableScroller;
      }
      
      public AddressesTableBean()
      {
      }
      
      public HtmlDataTable getAddressesTable() {
       return addressesTable;
      }
      
      public void setAddressesTable(HtmlDataTable addressesTable) {
       this.addressesTable = addressesTable;
      }
      
      
      }


      and addressesModel:
      public class AddressesModel implements Serializable{
      
      private static final long serialVersionUID = -3032793308402273197L;
      private ArrayList<Address> addressesTableData;
      private ArrayList<String> addressesTableLabels;
      
      
      @PostConstruct
      public void init(){
       this.addressesTableData = new ArrayList<Address>();
       Address tmp = new Address("012139|GeoPost|GmbH|Geo-Strasse|4|Fr|76312|Paris|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("014678|DPD|GmbH|Wailandtstraße|41|De|63741|Aschaffenburg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("015785|Amazon|GmbH|Goldenstreet|12a|US|572|Washington|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("047369|Kempf| |Kübelgasse|4|De|63741|Aschaffenburg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("074623|Ebay|GmbH|Moosweg|5|De|24356|Goldberg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("084786|Blubberlutsch|AG|Erpelgasse|44|De|76312|Entenhausen|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("012436|Opel|Ag|Kreidewegchen|5|De|23457|Kummerstadt|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("014678|DPD|GmbH|Wailandtstraße|41|De|63741|Aschaffenburg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("015785|Amazon|GmbH|Goldenstreet|12a|US|572|Washington|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("047369|Kempf| |Kübelgasse|4|De|63741|Aschaffenburg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("074623|Ebay|GmbH|Moosweg|5|De|24356|Goldberg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("084786|Blubberlutsch|AG|Erpelgasse|44|De|76312|Entenhausen|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("012436|Opel|Ag|Kreidewegchen|5|De|23457|Kummerstadt|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("014678|DPD|GmbH|Wailandtstraße|41|De|63741|Aschaffenburg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("015785|Amazon|GmbH|Goldenstreet|12a|US|572|Washington|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("047369|Kempf| |Kübelgasse|4|De|63741|Aschaffenburg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("074623|Ebay|GmbH|Moosweg|5|De|24356|Goldberg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("084786|Blubberlutsch|AG|Erpelgasse|44|De|76312|Entenhausen|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("012436|Opel|Ag|Kreidewegchen|5|De|23457|Kummerstadt|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("014678|DPD|GmbH|Wailandtstraße|41|De|63741|Aschaffenburg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("015785|Amazon|GmbH|Goldenstreet|12a|US|572|Washington|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("047369|Kempf| |Kübelgasse|4|De|63741|Aschaffenburg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("074623|Ebay|GmbH|Moosweg|5|De|24356|Goldberg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("084786|Blubberlutsch|AG|Erpelgasse|44|De|76312|Entenhausen|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("012436|Opel|Ag|Kreidewegchen|5|De|23457|Kummerstadt|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("014678|DPD|GmbH|Wailandtstraße|41|De|63741|Aschaffenburg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("015785|Amazon|GmbH|Goldenstreet|12a|US|572|Washington|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("047369|Kempf| |Kübelgasse|4|De|63741|Aschaffenburg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("074623|Ebay|GmbH|Moosweg|5|De|24356|Goldberg|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("084786|Blubberlutsch|AG|Erpelgasse|44|De|76312|Entenhausen|false|false");
       addressesTableData.add(tmp);
       tmp = new Address("012436|Opel|Ag|Kreidewegchen|5|De|23457|Kummerstadt|false|false");
       addressesTableData.add(tmp);
      }
      
      
      
      public AddressesModel(){
      }
      
      public ArrayList<String> getAddressesTableLabels() {
       return addressesTableLabels;
      }
      public void setAddressesTableLabels(ArrayList<String> addressesTableLabels) {
       this.addressesTableLabels = addressesTableLabels;
      }
      
      public ArrayList<Address> getAddressesTableData() {
       return addressesTableData;
      }
      
      public void setAddressesTableData(ArrayList<Address> addresses){
       this.addressesTableData = addresses;
      }
      
      public void addAddress(Address address) {
       addressesTableData.add(address);
      }
      
      
      
      
      
      }


      the Address-Class used in the ArrayList is just an ordinary bean.

      As you can see, what I'am trying to do is really simple, but for some reason, it just won't work.
      Any comments / critics / hints or help are/is welcome :-)

      best regards
      Chris

        • 1. Re: problem with datatable, sorting and datascroller
          ilya_shaikovsky

          bindings should be request scoped.

          • 2. Re: problem with datatable, sorting and datascroller
            shino

            Hi ilya :-)
            I applied your suggestion with the result, that the datascroller works all the time, but also the datatable falls back into its original, unsorted state.. which is what I expected ;-)
            do you any other possible causes for my problem?

            best regards
            chris



            • 3. Re: problem with datatable, sorting and datascroller
              ilya_shaikovsky

              you could control the sorting with sortOrder attribute bindings. and them could be of any scope. but in JSF component binsings itself should be request scoped.

              • 4. Re: problem with datatable, sorting and datascroller
                shino

                Since the sortOrder attribute is only available in the scrollableDataTable
                I gave it a try, but to no avail.. besides the scrollableDataTable
                has far too many features, most of which I don't want the user to have ;-)

                there is this simple example on the live demo page:
                http://livedemo.exadel.com/richfaces-demo/richfaces/sortingFeature.jsf?c=sorting&tab=usage

                and that is all I want.
                I cleaned up my view.xml and basically copied the code from the example so my view.xml now looks like this:

                <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                 xmlns:ui="http://java.sun.com/jsf/facelets"
                 xmlns:h="http://java.sun.com/jsf/html"
                 xmlns:f="http://java.sun.com/jsf/core"
                 xmlns:a4j="http://richfaces.org/a4j"
                 xmlns:rich="http://richfaces.org/rich">
                
                 <rich:panel header="#{msgs.addressOverviewTitle}"
                 style="height : auto; width : auto;">
                 <rich:spacer height="6"></rich:spacer>
                 <h:form>
                 <rich:dataTable
                 value="#{AddressesModel.addressesTableData}"
                 id="addressOverviewTable"
                 var="address"
                 columns="3"
                 rows="10"
                 reRender="addressTableDS"
                 headerClass="addressesTableHeader"
                 rowClasses="oddRow,evenRow" >
                 <rich:column sortBy="#{address.reference}">
                 <f:facet name="header">
                 <h:outputText value="#{AddressesModel.addressesTableLabels[0]}" />
                 </f:facet>
                 <h:outputText value="#{address.reference}" />
                 </rich:column>
                 <rich:column sortBy="#{address.name}">
                 <f:facet name="header">
                 <h:outputText value="#{AddressesModel.addressesTableLabels[1]}" />
                 </f:facet>
                 <h:outputText value="#{address.name}" />
                 </rich:column>
                 <rich:column sortBy="#{address.country}">
                 <f:facet name="header">
                 <h:outputText value="#{AddressesModel.addressesTableLabels[7]}" />
                 </f:facet>
                 <h:outputText value="#{address.country}" />
                 </rich:column>
                
                 <f:facet name="footer">
                 <rich:datascroller id="addressTableDS" maxPages="10" />
                 </f:facet>
                 </rich:dataTable>
                 </h:form>
                 <!-- <a4j:log popup="false" />-->
                 </rich:panel>
                </ui:composition>


                Just like in the example, there is no value binding anymore..
                which really makes me wonder where and how the sorting information is stored after one clicks on a sorting icon and jumps between pages..
                or does sorting the table replace the original arrayList with a new, sorted one??

                Also, just like in the live-demo, the only bean that is used now is my model bean, which of course still lives in session scope.

                However, when I sort the table and then jump to another page, all sorting information is lost again and I'm looking at my original table again.

                The source code of the capitalsBean used in the live demo also doesn't help me since I can't find that explains that behavior:
                capitalsBean:
                public class CapitalsBean {
                 private ArrayList<Capital> capitals = new ArrayList<Capital>();
                 private ArrayList<String> capitalsNames = new ArrayList<String>();
                 private List<SelectItem> capitalsOptions = new ArrayList<SelectItem>();
                 private String capital = "";
                
                 public List<Capital> autocomplete(Object suggest) {
                 String pref = (String)suggest;
                 ArrayList<Capital> result = new ArrayList<Capital>();
                
                 Iterator<Capital> iterator = getCapitals().iterator();
                 while (iterator.hasNext()) {
                 Capital elem = ((Capital) iterator.next());
                 if ((elem.getName() != null && elem.getName().toLowerCase().indexOf(pref.toLowerCase()) == 0) || "".equals(pref))
                 {
                 result.add(elem);
                 }
                 }
                 return result;
                 }
                
                 public CapitalsBean() {
                 URL rulesUrl = getClass().getResource("capitals-rules.xml");
                 Digester digester = DigesterLoader.createDigester(rulesUrl);
                 digester.push(this);
                 try {
                 digester.parse(getClass().getResourceAsStream("capitals.xml"));
                 } catch (IOException e) {
                 throw new FacesException(e);
                 } catch (SAXException e) {
                 throw new FacesException(e);
                 }
                 capitalsNames.clear();
                 for (Capital cap : capitals) {
                 capitalsNames.add(cap.getName());
                 }
                 capitalsOptions.clear();
                 for (Capital cap : capitals) {
                 capitalsOptions.add(new SelectItem(cap.getName(),cap.getState()));
                 }
                 }
                
                 public String addCapital(Capital capital) {
                 capitals.add(capital);
                 return null;
                 }
                
                 public ArrayList<Capital> getCapitals() {
                 return capitals;
                 }
                
                 public String getCapital() {
                 return capital;
                 }
                
                 public void setCapital(String capital) {
                 this.capital = capital;
                 }
                
                 public List<SelectItem> getCapitalsOptions() {
                 return capitalsOptions;
                 }
                
                 public ArrayList<String> getCapitalsNames() {
                 return capitalsNames;
                 }
                
                }


                for convinience, here's my AddressModel bean again:
                public class AddressesModel {
                
                private ArrayList<Address> addressesTableData;
                private ArrayList<String> addressesTableLabels;
                
                @PostConstruct
                public void init(){
                 this.addressesTableData = new ArrayList<Address>();
                 Address tmp = new Address("015785|Amazon|GmbH|Goldenstreet|12a|US|572|Washington|false|false");
                 addressesTableData.add(tmp);
                 //and so on...
                }
                
                
                
                public AddressesModel(){
                }
                
                public ArrayList<String> getAddressesTableLabels() {
                 return addressesTableLabels;
                }
                public void setAddressesTableLabels(ArrayList<String> addressesTableLabels) {
                 this.addressesTableLabels = addressesTableLabels;
                }
                
                public ArrayList<Address> getAddressesTableData() {
                 return addressesTableData;
                }
                
                public void setAddressesTableData(ArrayList<Address> addresses){
                 this.addressesTableData = addresses;
                }
                
                public void addAddress(Address address) {
                 addressesTableData.add(address);
                }
                }

                this is driving me mad.. I really don't get it... and I already spent far too much time on this simple problem.
                Any help is greatly appreciated

                best regards
                Chris

                P.S.: sorry for the horizontal scroll bar.. when I noticed it it was already too late and since there's no edit function for mortal board users ... =/

                • 5. Re: problem with datatable, sorting and datascroller
                  ilya_shaikovsky

                  1) sortOrder available for dataTable and extendedDataTable.
                  2)


                  which really makes me wonder where and how the sorting information is stored after one clicks on a sorting icon and jumps between pages..


                  it not stored. add sortOrder to columns and it should then.

                  3)

                  or does sorting the table replace the original arrayList with a new, sorted one??


                  no

                  • 6. Re: problem with datatable, sorting and datascroller
                    shino

                    hey ilya, thanks for staying with me ;-)

                    but still... according to [URL]http://www.jboss.org/file-access/default/members/jbossrichfaces/freezone/docs/devguide/en/html_single/index.html#extendedDataTable[/URL]
                    extendedDataTable doesn't have the sortOrder attribute... and even if it had, it isn't used in the live-demo example that I was reffering to. So there must
                    be a way to get this running with very simple components..

                    • 7. Re: problem with datatable, sorting and datascroller
                      ilya_shaikovsky

                      sorry for my very short description.. column has this attribute. And should be used.

                      as for livedemo page. - there sorting isn't stored. so its not used.

                      • 8. Re: problem with datatable, sorting and datascroller
                        shino

                        Thanks ilya, now I finally got it working :-)
                        This is how my column(s) now look like:

                        <rich:dataTable value="#{AddressesModel.addressesTableData}"
                         sortMode="single"
                         id="addressOverviewTable"
                         var="address"
                         columns="4"
                         rows="#{PreferencesBean.maxTableEntries}"
                         reRender="addressTableDS"
                         headerClass="addressesTableHeader"
                         rowClasses="oddRow,evenRow">
                         <rich:column sortBy="#{address.reference}"
                         sortOrder="#{AddressesModel.addressesSortOrders[0]}">
                         <f:facet name="header">
                         <h:outputText value="#{AddressesModel.addressesTableLabels[0]}" />
                         </f:facet>
                         <h:outputText value="#{address.reference}" />
                         </rich:column>
                         ## some more columns... ##
                         <f:facet name="footer">
                         <rich:datascroller id="addressTableDS" maxPages="10" />
                         </f:facet>
                        
                         </rich:dataTable>
                        


                        AddressesModel is a session scoped bean which holds an array of
                        org.richfaces.model.Ordering and thats all, no state saving of the table,
                        no component binding, just binding of the sortOrder attribute.

                        I was expecting/hoping that binding the table to a managed property
                        would also save the sorting state of the child columns of that table,
                        so that one could just interact with the table without having to care about
                        state saving at all. This was were I got confused, focusing only on the
                        table and winding up in my own speculations ;-) Thanks for helping me
                        out of this mess.