0

So I have this functionality which sends records Ids of a pageblocktable after selecting the checkbox. With pagination (next previous btns), when clicked next and going back to the previous offset, my old checkbox selections gets unchecked . The functionality of sending Ids to controller is working fine, all I need is just UI change that shows my checkbox selection even after toggling next and previous btns Any idea how do i retain the state of checkbox ? Thank you

1 Answer 1

1

As unhelpful and tautalogical as this'll sound at first, to retain the state of your checkbox you need to do something to retain that state. In other words, Salesforce doesn't do that for us and we need to do it ourselves.

I'll assume that you're using an instance of ApexPages.StandardSetController to take advantage of its included pagination controls. I'll further assume that you're using a wrapper class to enable you to add a "select me" checkbox which isn't backed by a field on your SObject.

The most common implementation, which has no memory of user input, would look something like this

public class MyExtension{
    public ApexPages.StandardSetController paginator;
    public List<Wrapper> wrapperList {get; set;}

    // Note that we take a StandardController here, not a StandardSetController
    public MyExtension(ApexPages.StandardController con){
        paginator = new ApexPages.StandardSetController(Database.getQueryLocator([SELECT Id, Name FROM Child_Object__c WHERE Parent__c = :con.getParameters().get('ParentId')]));

        paginator.setPageSize(10);
    }

    public List<Wrapper> getWrapperList(){
        wrapperList = new List<Wrapper>();
        for(Child_Object__c child :paginator.getRecords()){
            Wrapper wrapperInstance = new Wrapper();
            wrapperInstance.checkbox = false;
            wrapperInstance.record = child;

            wrapperList.add(wrapperInstance);
        }

        return wrapperList;
    }

    public class Wrapper{
        Boolean checkbox;
        Child_Object__c record;
    }
}

The issue here is that when you navigate to a different page, you're tossing out the input that you've gathered when you re-build the list of records to display.

To add "memory" to your page, you'll need to (at a minimum):

  • keep a record of previous information (before wiping out the wrapper list)
  • consult that previous information to set appropriate state (when building the new wrapper list)

For a simple checkbox, you could accomplish this by using a Set<Id> to track Ids of "selected" records.

public class MyExtension{
    public ApexPages.StandardSetController paginator;
    public List<Wrapper> wrapperList {set;}

    // We need to have somewhere to store the selected records that is tracked by
    //   the page's viewstate
    private Set<Id> selectedRecordIds;

    // Note that we take a StandardController here, not a StandardSetController
    public MyExtension(ApexPages.StandardController con){
        paginator = new ApexPages.StandardSetController(Database.getQueryLocator([SELECT Id, Name FROM Child_Object__c WHERE Parent__c = :con.getParameters().get('ParentId')]));

        paginator.setPageSize(10);

        selectedRecordIds = new Set<Id>();
    }

    public List<Wrapper> getWrapperList(){
        // Before wiping out wrapperList, store the current selected records
        for(Wrapper wrapperInstance :wrapperList){
            if(wrapperInstance.checkbox){
                selectedRecords.add(wrapperInstance.record.Id);
            }else{
                selectedRecords.remove(wrapperInstance.record.Id);
            }
        }

        wrapperList = new List<Wrapper>();

        for(Child_Object__c child :paginator.getRecords()){
            Wrapper wrapperInstance = new Wrapper();
            // Use the data we have to determine whether the wrapper's boolean
            //   field should be set to true or false as we build the new
            //   wrapperList
            wrapperInstance.checkbox = selectedRecords.contains(child.Id);
            wrapperInstance.record = child;

            wrapperList.add(wrapperInstance);
        }

        return wrapperList;
    }

    public class Wrapper{
        Boolean checkbox;
        Child_Object__c record;
    }
}

If it's not information that's stored on the SObject itself, we need to store the information in the page's viewstate. Only keeping the selected record Ids in the viewstate helps keep you under the 170kB limit.

The other thing to keep in mind that any GET requests (using the refresh button in your browser, using the forward/back buttons in your browser (probably), returning a non-null PageReference) will cause you to lose the viewstate.

Do note that I haven't tested this code, though I did base my approach on something that I have gotten to work in the past. I believe that you will be able to simply use {!paginator.nextPage}, {!paginator.lastPage} and the like with partial page updates just fine, so long as you use {!wrapperList} to feed your iteration component instead of {!paginator.getRecords}

2
  • Hey first of all thank you for a detailed reply, I am using OFFSETS and LIMITS in my dynamic SOQL and my query is not coming from constructor but from a method, and in VF it rerenders the search results with checkbox columns.
    – CcMda
    Nov 1, 2021 at 20:00
  • @CcMda The same principle still applies. You need to store which records have been selected in the viewstate, and then return the list of records indirectly (i.e. not simply return [query]; so you have a chance to re-mark previously selected records.
    – Derek F
    Nov 1, 2021 at 21:47

You must log in to answer this question.

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