Announcement

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

    Bug: Grouped ListGrid does not sort the groups ASC/DESC under certain circumstances

    Hi Isomorphic,

    please see this code (BuiltInDS-based, v9.1p_2014-07-24, FF26 Dev mode (I have a similiar setup in my application, there it is the same way in compiled mode in FF26 and Chrome 36)).

    BuiltInDS.java
    Code:
    package com.smartgwt.sample.client;
    
    import java.util.LinkedHashMap;
    
    import com.google.gwt.core.client.EntryPoint;
    import com.smartgwt.client.core.KeyIdentifier;
    import com.smartgwt.client.data.DataSource;
    import com.smartgwt.client.data.SortSpecifier;
    import com.smartgwt.client.types.SortDirection;
    import com.smartgwt.client.util.PageKeyHandler;
    import com.smartgwt.client.util.Page;
    import com.smartgwt.client.util.SC;
    import com.smartgwt.client.widgets.grid.GroupNode;
    import com.smartgwt.client.widgets.grid.GroupTitleRenderer;
    import com.smartgwt.client.widgets.grid.GroupValueFunction;
    import com.smartgwt.client.widgets.grid.ListGrid;
    import com.smartgwt.client.widgets.grid.ListGridField;
    import com.smartgwt.client.widgets.grid.ListGridRecord;
    import com.smartgwt.client.widgets.grid.SortNormalizer;
    
    public class BuiltInDS implements EntryPoint {
    	private ListGrid boundList;
    
    	public void onModuleLoad() {
    		KeyIdentifier debugKey = new KeyIdentifier();
    		debugKey.setCtrlKey(true);
    		debugKey.setKeyName("D");
    
    		Page.registerKey(debugKey, new PageKeyHandler() {
    			public void execute(String keyName) {
    				SC.showConsole();
    			}
    		});
    
    		boundList = new ListGrid(DataSource.get("animals"));
    		boundList.setWidth(1200);
    		boundList.setHeight(600);
    		boundList.setCanMultiSort(true);
    		boundList.setCanSort(true);
    		boundList.setShowFilterEditor(true);
    		boundList.setAutoFetchData(false);
    
    		ListGridField commonName = new ListGridField("commonName");
    		ListGridField scientificName = new ListGridField("scientificName");
    		scientificName.setSortNormalizer(new SortNormalizer() {
    			@Override
    			public Object normalize(ListGridRecord record, String fieldName) {
    				return record.getAttributeAsString(fieldName);
    			}
    		});
    
    		ListGridField lifeSpan = new ListGridField("lifeSpan");
    		ListGridField diet = new ListGridField("diet");
    		diet.setSortNormalizer(new SortNormalizer() {
    			@Override
    			public Object normalize(ListGridRecord record, String fieldName) {
    				// In order to sort the groups by ID, not name.
    				return getDietMap().get(record.getAttributeAsString("diet"));
    				// return record.getAttributeAsString("diet");
    			}
    		});
    		diet.setGroupValueFunction(new GroupValueFunction() {
    			@Override
    			public Object getGroupValue(Object value, ListGridRecord record, ListGridField field, String fieldName, ListGrid grid) {
    				return getDietMap().get(record.getAttributeAsString("diet"));
    				// return record.getAttributeAsString("diet");
    			}
    		});
    		diet.setGroupTitleRenderer(new GroupTitleRenderer() {
    			@Override
    			public String getGroupTitle(Object groupValue, GroupNode groupNode, ListGridField field, String fieldName, ListGrid grid) {
    				int i = groupNode.getGroupMembers().length;
    				String groupName = groupNode.getGroupMembers()[0].getAttributeAsString("diet");
    				return groupName + " (" + i + ((i == 1) ? " Animal)" : " Animals)");
    			}
    		});
    
    		boundList.setGroupByField("diet");
    		boundList.setFields(commonName, scientificName, lifeSpan, diet);
    		boundList.setSort(new SortSpecifier("diet", SortDirection.ASCENDING));
    		boundList.fetchData();
    		boundList.draw();
    	}
    
    	public static LinkedHashMap<String, Integer> getDietMap() {
    		LinkedHashMap<String, Integer> vM = new LinkedHashMap<String, Integer>();
    		vM.put("Carnivore", 1);
    		vM.put("Carnivore (mainly smaller fish)", 2);
    		vM.put("Crustaceans worms and mollusks", 3);
    		vM.put("Fish shrimp eels and squid", 4);
    		vM.put("Fruit and small vertebrates", 5);
    		vM.put("Fruits, Vegetation and Birds eggs", 6);
    		vM.put("Ground dwelling ants/termites", 7);
    		vM.put("Herbivore", 8);
    		vM.put("Insects and Larvae", 10);
    		vM.put("Insects, Fruit, seeds and grasses", 11);
    		vM.put("Insects, small reptiles and mammals", 12);
    		vM.put("Nectar and small insects", 13);
    		vM.put("Omnivore", 14);
    		vM.put("Plants and insects", 15);
    		// Test: If sorting by ID works, this has to be at the start/beginning
    		vM.put("Herbivore (Eucalyptus leaves)", 999);
    		return vM;
    	}
    }
    If you try to re-sort the ListGrid by diet=DESC (by clicking the column or using the context menu), you'll see that this does not work. If you ungroup, sorting by diet is working again.

    This seems to be connected to the setSortNormalizer and/or the setGroupValueFunction. If I change either of them to return the diet-string, sorting also works in grouped mode.

    Best regards,
    Blama

    #2
    Hi Blama
    It's an interesting side effect of the way grouping works - not exactly a bug though we agree that this particular case seems confusing.

    The issue is that when grouping occurs, a Tree structure is created with folders for each "group" - the "groupNodes".

    The appropriate field value for these nodes is set to the group-value - so in this case "diet" gets set to the result of "getGroupValue".

    The sorting logic is sorting all the records in the grid, including these group nodes. When the sortNormalizer runs, and looks at the "diet" field therefore, for group nodes it gets back the numeric values returned by the group-value function -- whereas for actual records it gets the raw "diet" values ("Carnivore", etc).

    So your solution here is to modify your sortNormalizer function to have a conditional for if (boundList.isGroupNode(record)) ... which realizes that the "diet" value is already numeric

    Regards
    Isomorphic Software

    Comment


      #3
      Hi Isomorphic,

      have you changed something in this area that would alter the default behaviour for the better?

      It seems that in my application I can get my desired behavoir by using a sortSpecifier of
      Code:
              setSortNormalizer(new SortNormalizer() {
                  @Override
                  public Object normalize(ListGridRecord record, String fieldName) {
                      if (listGrid.isGroupNode(record))
                          return null;
                      else
                          return record.getAttributeAsInt("STATUS_POSITION");
                  }
              });
      Please note that it also works for
      Code:
              setSortNormalizer(new SortNormalizer() {
                  @Override
                  public Object normalize(ListGridRecord record, String fieldName) {
                      if (listGrid.isGroupNode(record))
                          return record.getAttributeAsInt("STATUS_POSITION");
                      else
                          return record.getAttributeAsInt("STATUS_POSITION");
                  }
              });
      But in this case, if you put a breakpoint (DevMode) in Eclipse on the changed line and preview record.getAttributeAsInt("STATUS_POSITION") in Eclipse Debug "Expressions", it suggests that this will fail, but the outcome is as expected.

      I like the new (don't know when it changed, though) behaviour, but I'd also like to be sure that this is not just by chance/current data, but permanent.

      Best regards
      Blama

      Comment


        #4
        We continue to store the result of getGroupValue() as the field value for groupNodes.

        We have neither the data nor the getGroupValue() function in your new, partial code sample, but certainly, if the getGroupValue() function is returning the same thing as the field value, then it's not surprising that either code variant works.

        Comment


          #5
          Hi Isomorphic,

          using 5.1p and 6.1d (SNAPSHOT_v11.1d_2017-04-14) I have a issue with newly added values (add by now falling into the ListGrid's Criteria after a DSResponse) here. In my application the new records are just added at the end. In the sample it seems the LG unsorts. If you recreate the ListGrid, everything is in order again.

          This is the BuiltInDS.java:
          Code:
          package com.smartgwt.sample.client;
          
          import com.google.gwt.core.client.EntryPoint;
          import com.smartgwt.client.Version;
          import com.smartgwt.client.core.KeyIdentifier;
          import com.smartgwt.client.data.AdvancedCriteria;
          import com.smartgwt.client.data.Criterion;
          import com.smartgwt.client.data.DataSource;
          import com.smartgwt.client.data.SortSpecifier;
          import com.smartgwt.client.types.AutoFitWidthApproach;
          import com.smartgwt.client.types.GroupStartOpen;
          import com.smartgwt.client.types.OperatorId;
          import com.smartgwt.client.types.SortDirection;
          import com.smartgwt.client.util.Page;
          import com.smartgwt.client.util.PageKeyHandler;
          import com.smartgwt.client.util.SC;
          import com.smartgwt.client.widgets.IButton;
          import com.smartgwt.client.widgets.Window;
          import com.smartgwt.client.widgets.events.ClickEvent;
          import com.smartgwt.client.widgets.events.ClickHandler;
          import com.smartgwt.client.widgets.grid.GroupNode;
          import com.smartgwt.client.widgets.grid.GroupTitleRenderer;
          import com.smartgwt.client.widgets.grid.GroupValueFunction;
          import com.smartgwt.client.widgets.grid.ListGrid;
          import com.smartgwt.client.widgets.grid.ListGridField;
          import com.smartgwt.client.widgets.grid.ListGridRecord;
          import com.smartgwt.client.widgets.grid.SortNormalizer;
          import com.smartgwt.client.widgets.grid.events.DataArrivedEvent;
          import com.smartgwt.client.widgets.grid.events.DataArrivedHandler;
          import com.smartgwt.client.widgets.layout.VLayout;
          
          public class BuiltInDS implements EntryPoint {
              private VLayout mainLayout;
              private IButton recreateBtn;
          
              public void onModuleLoad() {
                  KeyIdentifier debugKey = new KeyIdentifier();
                  debugKey.setCtrlKey(true);
                  debugKey.setKeyName("D");
          
                  Page.registerKey(debugKey, new PageKeyHandler() {
                      public void execute(String keyName) {
                          SC.showConsole();
                      }
                  });
          
                  mainLayout = new VLayout(20);
                  mainLayout.setWidth100();
                  mainLayout.setHeight100();
          
                  recreateBtn = new IButton("Recreate");
                  recreateBtn.addClickHandler(new ClickHandler() {
                      @Override
                      public void onClick(ClickEvent event) {
                          recreate();
                      }
                  });
                  mainLayout.addMember(recreateBtn);
                  recreate();
                  mainLayout.draw();
              }
          
              private void recreate() {
                  Window w = new Window();
                  w.setWidth("95%");
                  w.setHeight("95%");
                  w.setMembersMargin(0);
                  w.setModalMaskOpacity(70);
                  w.setTitle("New ListGrid groups are not positioned correctly (" + Version.getVersion() + "/" + Version.getSCVersionNumber() + ")");
                  w.setShowMinimizeButton(false);
                  w.setIsModal(true);
                  w.setShowModalMask(true);
                  w.centerInPage();
          
                  final ListGrid animalsEditGrid = new AnimalsListGrid(true);
                  animalsEditGrid.fetchData();
                  w.addItem(animalsEditGrid);
          
                  final ListGrid animalsGroupGrid = new AnimalsListGrid(false);
                  animalsGroupGrid.fetchData(new AdvancedCriteria(new Criterion("lifeSpan", OperatorId.GREATER_OR_EQUAL, 60)));
                  w.addItem(animalsGroupGrid);
          
                  w.show();
              }
          
              private class AnimalsListGrid extends ListGrid {
                  public AnimalsListGrid(boolean grid1) {
                      super(DataSource.get("animals"));
                      setHeight100();
                      setAutoFetchData(false);
                      setAutoFitFieldsFillViewport(false);
                      setAutoFitWidthApproach(AutoFitWidthApproach.BOTH);
                      setAutoFitFieldWidths(true);
          
                      if (grid1) {
                          setCanEdit(true);
                      } else {
                          setGroupStartOpen(GroupStartOpen.ALL);
                          setSortByGroupFirst(true);
                          setSortField("commonName");
                          setGroupByField("diet");
          
                          // addDataArrivedHandler(new DataArrivedHandler() {
                          // @Override
                          // public void onDataArrived(DataArrivedEvent event) {
                          // SC.logWarn("In DataArrivedHandler");
                          // resort();
                          // }
                          // });
                      }
          
                      ListGridField commonName = new ListGridField("commonName");
                      ListGridField lifeSpan = new ListGridField("lifeSpan");
                      ListGridField status = new ListGridField("status");
          
                      ListGridField diet = new ListGridField("diet");
                      if (!grid1) {
                          diet.setHidden(true);
                      }
                      diet.setGroupValueFunction(new GroupValueFunction() {
                          @Override
                          public Object getGroupValue(Object value, ListGridRecord record, ListGridField field, String fieldName, ListGrid grid) {
                              // In order to sort the groups by position, not name.
                              return record.getAttributeAsInt("lifeSpan");
                          }
                      });
                      diet.setSortNormalizer(new SortNormalizer() {
                          @Override
                          public Object normalize(ListGridRecord record, String fieldName) {
                              return record.getAttributeAsInt("lifeSpan");
                          }
                      });
          
                      diet.setGroupTitleRenderer(new GroupTitleRenderer() {
                          @Override
                          public String getGroupTitle(Object groupValue, GroupNode groupNode, ListGridField field, String fieldName, ListGrid grid) {
                              int groupSize = grid.getGroupMembers(groupNode, true).length;
                              return groupValue.toString() + " (" + groupSize + ")";
                          }
                      });
          
                      setFields(commonName, lifeSpan, status, diet);
                      setSort(new SortSpecifier[] { new SortSpecifier(commonName.getName(), SortDirection.ASCENDING) });
                  }
              }
          }
          Steps to recreate:
          On start, the lower ListGrid should looks like this:
          Click image for larger version

Name:	Before.PNG
Views:	180
Size:	5.4 KB
ID:	244109

          Now if you edit another animal lifespan to 200, it looks like this (kinda unsorted, but still has the sort-arrow):
          Click image for larger version

Name:	After.PNG
Views:	178
Size:	6.7 KB
ID:	244110

          After close and reopen, the data is sorted as expected:
          Click image for larger version

Name:	AfterReopen.PNG
Views:	203
Size:	6.7 KB
ID:	244111




          I'm not exactly sure where the bug here is. It's either that the sort indicator stays or that the data is not sorted after the new record appeared.

          I also tried adding a DataArrivedHandler and calling resort(), which does not help, either, because it is not called after the edit DSResponse comes in (see commented out lines).

          Best regards
          Blama

          Comment


            #6
            Hi Isomorphic,

            is the new Custom Group Sorting feature in 12.1 you mention here related to this problem from #1? I think this should help here, shoundn't it?

            Best regards
            Blama

            Comment


              #7
              It’s not clear what was going on in this thread - it’s pretty old.

              The new feature allows more control over grouped sorting. If this issue is still happening in the latest, and you have no clearer idea what’s wrong, using custom group sorting could give you a workaround.

              Comment

              Working...
              X