Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Sorting in a ListGrid

    Hi,

    I have a field in a listGrid with contents like this:
    "1 A"
    "2 A"
    "10 A".

    When sorting the listGrid to this field, it is sorted in a string-based way:
    "1 A"
    "10 A"
    "2 A"

    Is it possible to sort it like this:
    "1 A"
    "2 A"
    "10 A" ?

    Using smartGWT 4.0p Power.

    (Edit: I mean the user-sorting, i.e. when he clicks on the field title).
    Last edited by edulid; 30 Dec 2013, 08:22.

    #2
    Yes, you can add a SortNormalizer to a ListGridField to customize the approach used for sorting.

    Comment


      #3
      Originally posted by Isomorphic View Post
      Yes, you can add a SortNormalizer to a ListGridField to customize the approach used for sorting.
      Thanks. But what to return ?
      The javadocs of SortNormalizer say:
      Code:
      normalized value for sorting (a java numeric primitive type or String)
      but what to return in this case? I guess an integer, in this case the first part of the field.. but what if I also want to sort using the second part of the field?
      So:

      Sort
      "1 A" --> Return 1
      "2 A" --> Return 2
      "10 A" --> Return 10
      "1 B" -->Return 1

      So it will sort (maybe) like this:
      "1 B"
      "1 A"
      "2 A"
      "10 A"
      In this case,
      "1 B"
      "1 A"
      is not sorted correctly. So what to return in the SortNormalizer for this case?

      Comment


        #4
        Return whatever values would sort in the order you want. It doesn't matter what the values are, they won't be shown to the end user.

        Comment


          #5
          since I don't know how many values I have, I cannot return something like this:

          "1 A" --> Return 1
          "2 A" --> Return 3
          "10 A" --> Return 4
          "1 B" -->Return 2

          So I have to know how to map a value e.g. "2 A" to a value, without knowing anything else.

          And I don't have any other ideas what to return.

          Comment


            #6
            Well, you seem to be saying that you have a sorting order that cannot be computed. We can't help with that :)

            In reality however, this is easily done. There is no requirement that your sortNormalizer returns an actual sort position, it can return *any value* that sorts into the proper order.

            So you could, for instance, take all values that begin with B and start them at 1000. The ideal approach would of course divide up JavaScript's MAX_INT value into 26 bins, one per letter..

            Comment


              #7
              Thanks for your answer, I will take a look at your approach. But the first thing that comes into my mind after reading your approach is: what will happen with variable string lengths?
              For example:
              I have
              "1 AAC"
              "1 AACA"
              "1 AABA"

              I basically want to sort the field first numerically using the first part of the field, then string-based using the second part of the string. The second part of the string may be *any* string. Would your approach work in this case?

              By the way, why didn't you implement the SortNormalizer using some kind of Comparator?

              Comment


                #8
                We're not sure on the confusion - the SortNormalizer's normalize() method is a function that accepts a record and a fieldName, and is expected to return some value applicable to the value of the passed field and record, but which is suitable for sorting.

                In your case, one very simple mechanism would be to have your normalize() method collect the value at record[fieldName], split it into two strings at the space, convert the first part to a number and pad it to 10 chars with leading zeros, and then append the second part to this padded number.

                A normalizer that returns a string in this format (eg, 0000000123ABCD) will sort the way you want.

                Comment


                  #9
                  Ah, you return a string, that was my confusion I think. This makes sense!

                  I just read the following:
                  Code:
                  Note that, if the dataset exceeds dataPageSize and hence paging is introduced, the grid relies on the server to provide sorting, and the sortNormalizer will no longer be called.
                  So I think this will not work for my case, where I have more than 75 records to be sorted.
                  So what could be an approach for this case?

                  I could create a new field in the datasource, e.g. "sortingField", which is a string in the format you proposed. But is it possible to let the listGrid be sorted in respect to "sortingField" when the user clicks the title of field "toBeSorted"? So to let the listGrid be sorted in respect to ANOTHER (invisible) field if a field's title is clicked?
                  Last edited by edulid; 4 Jan 2014, 02:19.

                  Comment


                    #10
                    One way would be to show the sortField in the grid instead of the real field - and on it, set displayValueFromRecord:true and displayField:realFieldName

                    Comment


                      #11
                      Thanks for the new approach, sounds great. But it is unfortunately not working.

                      It is sorting with respect to the displayField, which is the realFieldName, so basically it is still sorting with respect to the real field, NOT to the sorting field.

                      Testing Module:
                      Code:
                      public class TestingModule implements EntryPoint {
                      
                      	public void onModuleLoad() {
                      
                              final ListGrid countryList = new ListGrid();  
                              countryList.setWidth(500);  
                              countryList.setAlternateRecordStyles(true);  
                              countryList.setDataSource(DataSource.get("schueler_table"));
                        
                              ListGridField name = new ListGridField("f_name", "Last name");  
                              ListGridField vorname = new ListGridField("f_vorname", "First name");  
                              vorname.setDisplayValueFromRecord(true);
                              vorname.setDisplayField("f_name");
                              countryList.setHeight(300);
                        
                              countryList.setFields(name,vorname);  
                              countryList.setAutoFetchData(true);
                        
                              countryList.draw();
                          }
                      Schueler_table.ds.xml
                      Code:
                      <DataSource ID="schueler_table" serverType="sql" tableName="t_schueler"  >
                      	<fields>
                      		<field name="f_schueler_id" type="sequence" primaryKey="true" hidden="true" />
                      		<field name="f_name" type="text"  />
                      		<field name="f_vorname" type="text" />
                      		
                      
                      	</fields>
                      
                      </DataSource>
                      Here, the realField=displayField=Last Name=f_name.
                      The sorting field=First name=f_vorname
                      Code:
                      vorname.setDisplayValueFromRecord(true);
                              vorname.setDisplayField("f_name");
                      When I click on it, it sorts with respect to "f_name", which is the "displayed field", and NOT the "sorting field".

                      You can see this behavior in the sql being executed:
                      Code:
                      SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY f_name) AS rowID FROM (SELECT TOP 100 PERCENT  t_schueler.f_name, t_schueler.f_schueler_id, t_schueler.f_vorname FROM t_schueler WHERE ('1'='1') ORDER BY t_schueler.f_name) x) y WHERE y.rowID BETWEEN 1 AND 75
                      ....

                      (ORDER BY f_name)
                      But it should be:
                      (ORDER BY f_vorname), which is the sorting field.

                      Comment


                        #12
                        See the doc for sortByDisplayField

                        Comment


                          #13
                          Your solution seemed to work fine until I started to get some very strange "timeout errors".

                          I have a listGrid with dynamic fields, i.e. the user selects the fields he wants to see from a menu.

                          Then this code is called:
                          Code:
                          setFields(f.toArray(new ListGridField[] {}));
                          where f is the list of fields the user wants to see.

                          In order to sort, I wrote this code:
                          Code:
                          field.setDisplayValueFromRecord(true);		field.setDisplayField("klasse_text");
                          field.setSortByDisplayField(false);
                          As I told you, everything works fine. The field is sorted correctly.

                          BUT I see on the server a new DSRequest:

                          Code:
                          {
                              dataSource:"schueler", 
                              operationType:"fetch", 
                              componentId:"isc_PickListMenu_1", 
                              data:{
                              }, 
                              textMatchStyle:"startsWith", 
                              resultSet:[ResultSet ID:isc_ResultSet_3 (created by: isc_PickListMenu_1)], 
                              callback:{
                                  caller:[ResultSet ID:isc_ResultSet_3 (created by: isc_PickListMenu_1)], 
                                  methodName:"fetchRemoteDataReply" 
                              }, 
                              willHandleError:true, 
                              showPrompt:false, 
                              prompt:"Finding Records that match your criteria...", 
                              oldValues:{
                              }, 
                              requestId:"schueler$62710", 
                              clientContext:{
                                  requestIndex:1
                              }, 
                              fallbackToEval:false, 
                              componentContext:"isc_DynamicForm_3.klasse", 
                              lastClientEventThreadCode:"XRP5[E]", 
                              bypassCache:true
                          }
                          which has after some time a timeout. I cannot explain myself where this comes from.
                          I watched the components, looking for the component "isc_PickListMenu_1", and I found it: it is an invisible component I never created! So this component is doing a request which never concludes: a time out is being returned by the server.

                          When I delete these lines:
                          Code:
                          field.setDisplayValueFromRecord(true);		field.setDisplayField("klasse_text");
                          field.setSortByDisplayField(false);
                          there is NO TIMEOUT, and NO component "isc_PickListMenu_1" is being created.

                          I haven't been able to reproduce this in a simple test case,.. but maybe you have an idea why this may be happening?
                          Why is this component creating itself when I call this code ? This is very strange.

                          I get this error in the console when the timeout occurs:
                          Code:
                          08:59:32.176 [ERROR] [zedes2] 08:59:32.158:TMR2:WARN:RPCManager:getHttpHeaders called with a null XmlHttpRequest object
                          com.smartgwt.client.core.JsObject$SGWT_WARN: 08:59:32.158:TMR2:WARN:RPCManager:getHttpHeaders called with a null XmlHttpRequest object
                              at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
                              at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
                              at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
                              at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
                              at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:105)
                              at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
                              at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
                              at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:293)
                              at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:547)
                              at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)
                              at java.lang.Thread.run(Thread.java:662)
                          Code:
                          08:59:32.473 [ERROR] [zedes2] 08:59:32.461:TMR2:WARN:RPCManager:Operation timed outundefined - response: {operationId: "schueler_fetch", clientContext: Obj, context: Obj, transactionNum: 9, httpResponseCode: undef, httpResponseText: undef, xmlHttpRequest: undef, transport: "xmlHttpRequest", status: -100, clientOnly: undef, httpHeaders: undef, isStructured: true, callbackArgs: null, results: Obj, data: "Operation timed out", startRow: 0, endRow: 0, totalRows: 0}
                          com.smartgwt.client.core.JsObject$SGWT_WARN: 08:59:32.461:TMR2:WARN:RPCManager:Operation timed outundefined - response: {operationId: "schueler_fetch", clientContext: Obj, context: Obj, transactionNum: 9, httpResponseCode: undef, httpResponseText: undef, xmlHttpRequest: undef, transport: "xmlHttpRequest", status: -100, clientOnly: undef, httpHeaders: undef, isStructured: true, callbackArgs: null, results: Obj, data: "Operation timed out", startRow: 0, endRow: 0, totalRows: 0}
                              at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
                              at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
                              at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
                              at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
                              at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:105)
                              at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
                              at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
                              at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:293)
                              at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:547)
                              at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)
                              at java.lang.Thread.run(Thread.java:662)
                          As I said, if I delete the code, no timeout occurs and this error doesn't appear in the console.

                          Using smartgwtpower-4.0p-2013-11-27. I also tried with the new nightly (5.1.14) and with SmartGWT 4.1, but it's the same.
                          Last edited by edulid; 6 Jan 2014, 07:43.

                          Comment


                            #14
                            Any idea Isomorphic?

                            Comment


                              #15
                              We'll take a look at the mysterious extra component and fetch in this case and update this thread with our findings.

                              In the meantime, there are probably other ways to do this - you could customize the SQL for the operation, or the customSelect/UpdateExpressions for the DS fields, or you could use transformRequest/Response to map between the real-value and the "sort" value. If you have the freedom to do so, you could also change to storing the "sort" value in your data, and add a CellFormatter and an EditValueFormatter to the ListGridField that both show the "real" string by un-formatting the "sort" string.

                              Comment

                              Working...
                              X