3

I have created a th/td HTML table where Wrapper class List is already sorted with Comparable interface.

Requirement - User should be able to sort it in descending order based on the same field as well as back to ascending order if required.

What would be the options for doing this change?

  1. A select list option with ascending/descending and calling method through ActionFunction?
  2. Anything like commandLink possible in HTML table where user clicks the label of the column to be sorted and they alternately sort as asc and desc?
  3. Any other suggestions ?

Updating code :

Obj To Compare method

  public ProdWrapper(){
    }
    public ProdWrapper(Integer experienceNumberInt){
        this.AccountNumber = AccountNumber ;

    }
    public Integer compareTo(Object ObjToCompare) {
        ProdWrapper compareNum = (ProdWrapper)ObjToCompare;
         if (AccountNumber > compareExpNum .AccountNumber ) {
    return ascending ? 1 : -1;
} else if(AccountNumber < compareExpNum .AccountNumber ) {
    return ascending ? -1 : 1;
} else {
    return 0;
}      
}      

CommandLink

<th><apex:commandLink value="Account #" action="{!changeSort}" ><apex:param value="exp"/></apex:commandLink></th>
2
  • 2
    Most user friendly is #2 which I prefer, having a link in header to toggle sort!
    – Raul
    Feb 20, 2017 at 18:45
  • 1
    Just to add, if its a HTML table, JavaScript remoting or remote objects is better option for performance than with apex:actionFunction.
    – Raul
    Feb 20, 2017 at 18:53

1 Answer 1

5

If you invert the sign of your Comparable, you'll get descending order. Depending on your exact requirements, this is usually pretty easy. Consider the following code:

public class MyController {
  public Boolean sortAsc { get; set; }
  public String sortField { get; set; }

  // Static vars don't persist, so we use backing variables
  Boolean mSortAsc;
  String mSortField;

  // What we display on the page
  public Wrapper[] WrapperList { get; set; }
  public class Wrapper implements Comparable {
    // Implementation can be whatever you need
    public Integer compareTo(Object other) {
      Wrapper o = (Wrapper)other;
      Integer baseSortValue = ...; // calculate normal value
      return baseSortValue * (MyController.sortAsc? 1: -1); // Multiply by -1 to invert sort;
    }
  }
  public void changeSort(String field) {
    // Set static vars
    sortAsc = mSortAsc;
    sortField = mSortField;
    // Set asc on field change, otherwise toggle
    sortAsc = field != sortField || !sortAsc; 
    sortField = field;
    WrapperList.sort();
    // And store for next sort
    mSortAsc = sortAsc;
    mSortField = sortField;
  }
}

I've left out some details, obviously, but this should point you in the right direction. Generally, I use apex:commandLink in the header cells to call changeSort, which takes care of all of the logic.


Edit: More Code

To sort by a column, just set the parameter for which field you wish to sort:

<apex:column >
    <apex:facet name="header">
        <apex:commandLink status="status" action="{!resort}" reRender="form">
            <apex:param assignTo="{!newSortOrder}" value="Owner.Name" />
            Assigned To {!if(sortField='Owner.Name',if(sortAsc,'▲','▼'),'')}
        </apex:commandLink>
    </apex:facet>
    <apex:outputField value="{!record.Owner.Name}">
        <apex:inlineEditSupport />
    </apex:outputField>
</apex:column>

In the controller, there is a public String sortField { get; set; } and public Boolean sortAsc { get; set; }; the former is set by apex:param, the latter is used by the resort method (above called changeSort). This particular implementation uses a SOQL ORDER BY clause, but you can use dynamic SObject methods to use the Comparable interface.

Also, see my answer on this question for how you could use an Interface to define which field is sorted (basically, you'd create a bunch of sorting interfaces, then select one to use). For example:

public compareTwo sortOrder;

public class Wrapper implements Comparable {
    public Integer compareTo(Object other) {
        return (sortAsc? 1: -1) * sortOrder.compare(this, (Wrapper)other);
    }
}

public interface compareTwo {
    Integer compare(Wrapper item1, Wrapper item2);
}

public class AccountNumberSort implements compareTwo {
    public Integer compare(Wrapper item1, wrapper item2) {
        return item1.AccountNumber.compareTo(item2.AccountNumber);
    }
}
public class NameSort implements compareTwo {
    public Integer compare(Wrapper item1, wrapper item2) {
        return item1.Name.compareTo(item2.Name);
    }
}

...

public void changeSort() {
    sortAsc = sortField != mSortField || !sortAsc;
    mSortField = sortField;
    mSortAsc = sortAsc;

    Map<String, Type> fieldMap = new Map<String, Type> {
        'AccountNumber' => AccountNumberSort.class,
        'Name' => NameSort.class
    };
    sortOrder = (compareTwo)fieldMap.get(sortField).newInstance();
    wrapperList.sort();
}
4
  • Hi @sfdcfox. Thanks for your answer. I have updated my code above . Can you please elaborate a little more on how to pass the sort direction variable through commandlink?
    – Dexter D
    Feb 21, 2017 at 19:33
  • @DexterD There's plenty of examples of passing parameters via commandLink. Where are you stuck?
    – sfdcfox
    Feb 21, 2017 at 20:14
  • Yes thanks @sfdcfox. It's done . Question - Can the sorting be done based on second field as well? e.g Primary column being say XYZ Number and second column being Role . So if XYZ is same but role is different , sorting will be done based on Role column among these XYZ number rows. Is this possible in Comparable Interface?
    – Dexter D
    Feb 22, 2017 at 16:33
  • @DexterD You can sort on however you like, you just need to add some more functionality. I added a link to an example that uses an interfaced sort, plus an example that is more directly applicable.
    – sfdcfox
    Feb 22, 2017 at 16:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .