1

I currently have a Visualforce page embedded on the contact record that displays all active campaigns with a chexckbox indicating if the contact is a member of the campaign (if checked then the contact is a member). I also have a command button to save changes. The visualforce code is generated using an apex:repeat like as so:

<apex:repeat value="{!campaignWrappers}" var="cm">
  <div class="MailingRow">
     <apex:outputText styleClass="CampaignLabel" value="{!cm.c.Name}" ></apex:outputText>
     <apex:inputCheckbox styleClass="CampaignCheck" value="{!cm.selected}" />
  </div>
</apex:repeat>

My question is, what is the best way to go about passing the checkbox values to the controller? Ideally I would like to pass across only checkboxes that have been changed, passing across a list of those that have been gone from checked to unchecked and a second list that contains the checkboxes that have gone from unchecked to checked. I can then insert and delete these lists.

Is this a case where an actionSupport tag should be used on the checkbox?

<apex:actionSupport event="onChange" action="{!updateChangeList}" />
Ronnie
  • 1,053
  • 5
  • 18
  • 30

2 Answers2

2

You can use a Wrapper class for contact object to store the current and new status of the contact to check whether the contact is member of campaign.

public class ContactWrapper 
{                 
 public Boolean checked{ get; set; }            
 public Contact con { get; set;}                     

 public AccountSKUWrapper()
 {                
   con = new Contact();                
   checked = false;            
 }                     

 public ContactWrapper(Contact c)
 {                
   con= c;                
   checked = c.selected;            
 }                               

}

Using selected field we can identify whether contact is a member of Campaign and checked variable in the wrapper helps in identifying what input given in the VF page. Create the list of ContactWrapper and initialize each ContactWrapper object using Overloaded Contructor "ContactWrapper(Contact c)".

    <apex:repeat value="<ContactWrapper Object>" var="cm">
  <div class="MailingRow">
     <apex:outputText styleClass="CampaignLabel" value="{!cm.con.Name}" ></apex:outputText>
     <apex:inputCheckbox styleClass="CampaignCheck" value="{!cm.checked}" />
  </div>
</apex:repeat>

During save button click, follow below steps: Check the value of checked variable differing from selected field.

Create 2 list **CHANGEDCONTACTLIST** and **UNCHANGEDCONTACTLIST** and then Loop through the list of ContactWrapper

begin loop
1.If checked differs then load **CHANGEDCONTACTLIST** with Contact Wrapper object as this need to be updated in Contact object; in order to update selected field of contact. 

2.Else load **UNCHANGEDCONTACTLIST**.
end loop

Eventually using these two list CHANGEDCONTACTLIST and UNCHANGEDCONTACTLIST. you can do insert or delete dml.

I believe there is no use of ActionSupport tag here as all the values required will be used (read/write) using getter and setter of the ContactWrapper list.

public List<ContactWrapper> wrapper {get;set;}
kapcsbit
  • 107
  • 6
1

Whole collection will be sent back to the server (so state of all checkboxes). That's not too different between Salesforce and any other web-related language (see Get $_POST from multiple checkboxes for example. Main difference is that usually you'd use a variable with name ending in [] and SF will use unique names with incrementing number somewhere in the name).

If you really want to build 2 lists - maybe some javascript magic of adding/removing content of 2 hidden text fields... but that's bit messy.

When the form will be submitted and your updateChangeList() gets called you could iterate the collection of CampaignWrappers and compare with previous value. Either store the previous value in 1 more variable in the wrapper & not expose it to the user or you'll still know based on what was in the Campaign:

Set<Id> campaignsToBeAddedTo = new Set<Id>();
Set<Id> campaignsToBeRemovedFrom = new Set<Id>();

for(CampaignMember cm : campaignWrappers){
   if(cm.selected && cm.c.CampaignMembers.isEmpty(){
      // is ticket but wasn't a member before
      campaignsToBeAddedTo.add(cm.c.Id);
   } else if (!cm.selected && !cm.c.CampaignMembers.isEmpty()){
      // not ticked but used to be a member
      campaignsToBeRemovedFrom.add(cm.c.Id);
   }
}

You could even optimize it a bit (like put isEmpty() checks to helper variable). Or read about setter methods that can have "side effects". After all a loop like that will be run for you anyway when form data will be deserialized. http://blogs.developerforce.com/developer-relations/2008/06/property-access.html is a nice starting point (it's ancient, I know. Basically before this blog post we had to write Boolean getSelected() and void setSelected(Boolan b) for each variable we wanted to use in VF).

last but not least - a link to page execution order.

Community
  • 1
  • 1
eyescream
  • 18,088
  • 2
  • 34
  • 46
  • Thanks for the info eyescream. I'm slightly confused. So using actionSupport on each checkbox is not the way forward, instead I would have an action on my commandbutton which does the logic as per your exampel above. Would I need to set an ID for each checkbox so that I can identify them in the controller? I'm unable to set the ID in visualforce like so: id={!cm.c.id} as it must be a literal? – Ronnie Apr 15 '13 at 13:33
  • ActionSupport is not a magic wand ;) It's a AJAX shortcut to have whole form submitted & action executed. Treat it as immediate clicking on some hidden button the moment user selects a checkbox. The point is it'll be a network traffic. If you want to send them one by one - sure, go for it. You can but don't have to set Ids. They'll get `id` and `name` set automatically. Just don't worry, assume that when method body is entered all variables will be parsed etc. for you, ready to use – eyescream Apr 15 '13 at 15:04
  • Thanks for the detailed explanation eyescream! – Ronnie Apr 16 '13 at 08:11