[SalesForce] Is it possible to select individual records when using standardSetController in a custom controller

Background: I've been working on a dynamic contact search page modeled on Jeff Douglas 2010 post.

It works, and now I am exploring ways to add pagination and the ability to select records and take actions.

Was reading 'visualforce in practice' and on p62 they demonstrate how to use a standardSetController inside a custom controller to provide pagination and other functionality.

Question Started fresh using the standardSetController, and Pagination is working well, but I have not been able to figure out how to pass a selected list of records back to the controller so that I could add the selected records to a campaign.

I've basically copied the code from the book and just switched it up to work on contacts and added an apex:inputcheckbox field. It seems like the standard set controller methods getSelected / setSelected methods would allow me to do this, but I cant pass selected records back to the controller successfully.

I've seen some examples of using a wrapper class for this type of functionality, but want to confirm that its not possible to use the standardSetController methods before heading down that road.

Visualforce Page

<apex:page controller="CustomSearchListController">
    <apex:form >
        <apex:pageBlock >
            <apex:pageBlockTable value="{!ssc.Records}" var="c" rows="25">
                <apex:column ><apex:inputCheckbox value="{!ssc.Selected}"/>     
                </apex:column>
                <apex:column value="{!c['firstname']}"/>
                <apex:column value="{!c['lastname']}"/>
            </apex:pageBlockTable>
            <apex:panelGrid columns="3">
                <apex:commandLink action="{!ssc.Previous}" value="previous page" rendered="{!ssc.HasPrevious}"/>
                <apex:commandLink action="{!ssc.Next}" value="Next Page" rendered="{!ssc.HasNext}"/>
                <apex:commandButton action="{!addToCampaign}" value="Add to campaign"/>
            </apex:panelGrid>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Controller

public with sharing class CustomSearchListController {

    public ApexPages.StandardSetController ssc {get; private set;}

  // init the controller and display some sample data when the page loads
  public CustomSearchListController() {
    ssc = new ApexPages.StandardSetController([SELECT firstname, lastname, account.name, Fellows_Status__c,id, account.id from contact LIMIT 1000]);
      }

    public list<contact> selContacts {get;set;}

    public pageReference addToCampaign(){
        system.debug('button clicked');
        list<campaignMember> cml = new list <campaignmember>();
        campaignmember cm = new campaignMember();
        selContacts = ssc.getSelected();
        system.debug('ssc' + ssc.getSelected());
        system.debug('selcontacts' + selContacts);
        FOR(contact c: selContacts){
            cm.ContactId = c.Id;
            cm.CampaignId = '70140000000XZt6AAG' ; //hard coding campaignID for testing purposes
            cml.add(cm);
        }
        insert cml;
        return null;
    }

Best Answer

Weighing in with a not so nice but working workaround to use the standard set controller for the pagination yet have the ability to select records and pick these up within your controller without a wrapper class. (posting as an answer as i don't yet have the rep to comment below the question to suggest in a quick comment)

I created a Read Only checkbox field on the Contact object called SelectedInUI__c which was hidden from the layout and read only, its sole purpose being to give a selectable option on each SObject within the StandardSetController.getRecords() collection.

I then tied in this field to the VF page and controller given in your example.

VF Page

<apex:page controller="CustomSearchListController">
<apex:form >
    <apex:pageBlock >
        <apex:pageBlockTable value="{!ssc.Records}" var="c" rows="25">
            <apex:column >
                <apex:inputCheckbox value="{!c['SelectedInUI__c']}"/>
                <!--<apex:inputCheckbox value="{!ssc.Selected}"/>-->
            </apex:column>
            <apex:column value="{!c['firstname']}"/>
            <apex:column value="{!c['lastname']}"/>
        </apex:pageBlockTable>
        <apex:panelGrid columns="3">
            <apex:commandLink action="{!ssc.Previous}" value="previous page" rendered="{!ssc.HasPrevious}"/>
            <apex:commandLink action="{!ssc.Next}" value="Next Page" rendered="{!ssc.HasNext}"/>
            <apex:commandButton action="{!addToCampaign}" value="Add to campaign"/>
        </apex:panelGrid>
    </apex:pageBlock>
</apex:form>

Controller

public with sharing class CustomSearchListController {

    public ApexPages.StandardSetController ssc {get; set;}

    // init the controller and display some sample data when the page loads
    public CustomSearchListController() {
        ssc = new ApexPages.StandardSetController([SELECT firstname, lastname, account.name, id, account.id, SelectedInUI__c from contact LIMIT 1000]);
        for(SObject contact : ssc.getRecords())
        {
            /*
            *    Within the context of the controller set the Boolean field for all records to false
            *    if no updates to the Contact record are occuring setting the local context should be okay. 
            */
            contact.put('SelectedInUI__c', false);
        }
    }

    public list<contact> selContacts {get;set;}

    public pageReference addToCampaign()
    {
        list<campaignMember> cml = new list <campaignmember>();

        for(SObject contact : ssc.getRecords())
        {
            /*
            *    As all values earlier in the controller were set to false, only user selected records will meet the following criteria.
            */
            if((Boolean)contact.get('SelectedInUI__c'))
            {
                campaignmember cm = new campaignMember();
                cm.ContactId = contact.Id;
                cm.CampaignId = '70158000000KIXI' ; //hard coding campaignID for testing purposes
                cml.add(cm);
            }
        }
        insert cml;
        return null;
    }
}

As i said, its not the nicest solution but it allows you to use the pagination from the StandardSetController with the line by line selection ability desired.

Related Topic