Monday, May 27, 2013

Bug with apex:selectCheckboxes

The Project:
Make a search with multi-select checkboxes for better filtering. 

The Solution:
Simple right? Just make a page and controller with some <apex:selectCheckboxes> and a <apex:commandButton> to make it all go. 

<apex:page showHeader="true" sidebar="true" controller="sfg_testBugWithActionButton">
<apex:form>
    <apex:outputpanel id="mainWrap">
        <apex:repeat value="{!filterMap}" var="key">
          <div class="filterItem">
            <h2>{!key}</h2>
            <apex:selectCheckboxes value="{!filterKeys[key]}" layout="pageDirection">
              <apex:selectOptions value="{!filterMap[key]}" />
            </apex:selectCheckboxes>
          </div>
        </apex:repeat>
        <apex:commandButton action="{!preformAction}" rerender="renderWrap" value="Submit Action" />
      <apex:outputpanel id="renderWrap">
        {!resultString}
      </apex:outputpanel>
    </apex:outputpanel>
  </apex:form>
</apex:page>



public class sfg_testBugWithActionButton {

  public List<String> fGender {get; set;}

  public List<String> fGrade {get; set;}

  public List<String> fRole {get; set;}

  public String resultString {get; set;}



  public Map<String, List<SelectOption>> filterMap {get; set;}
  public Map<String, String> filterKeys {get; set;}

  public sfg_testBugWithActionButton() {
    filterKeys = new Map<String, String> {
      'Gender' => 'fGender',
      'Grade' => 'fGrade',
      'Role' => 'fRole'
    };
    createfilterMap();
    resultString = 'on Load of page';
  }

  public PageReference preformAction() {
    resultString = 'button action preformed';
    return null;
  }

  private void createfilterMap() {
    filterMap = new Map<String, List<SelectOption>>();
    List<SelectOption> options = new List<SelectOption>();

    for (String s : filterKeys.keySet()) {
      if (s == 'Gender') {
        options = new List<SelectOption>();
        options.add(new SelectOption('Male', 'Male'));
        options.add(new SelectOption('Female', 'Female'));
        filterMap.put('Gender', options);
      }
      if (s == 'Grade') {
        options = new List<SelectOption>();
        options.add(new SelectOption('A', 'A'));
        options.add(new SelectOption('B', 'B'));
        options.add(new SelectOption('C', 'C'));
        filterMap.put('Grade', options);
      }
      if (s == 'Role') {
        options = new List<SelectOption>();
        options.add(new SelectOption('Support', 'Support'));
        options.add(new SelectOption('Sales', 'Sales'));
        options.add(new SelectOption('Marketing', 'Marketing'));
        filterMap.put('Role', options);
      } 
    }
  }
}

(no style so nothing pretty, just to get the idea)

But WAIT! Why isn't anything happening when I click the button? The text next to the button should be changing. hummm Lets put a debug at the top of the method being called by the button and check the logs to see if its hit...

public PageReference preformAction() {
  system.debug('Gender: ' + fGender);
  system.debug('Grade: ' + fGrade);
  system.debug('Role: ' + fRole);
  resultString = 'button action preformed';
  return null;
}

hummm nothing.. not even a mention of the button getting clicked in the logs, let alone my debug statements. Ok I know from experience I can force the button by using the <apex:actionRegion> attribute..

<apex:actionRegion>
  <apex:commandButton action="{!preformAction}" rerender="renderWrap" value="Submit Action" />
</apex:actionRegion>
      
Click and well now the debug statement is being hit, but its not picking up the values in the  <apex:selectCheckboxes> hummmm.. I know I shouldnt need the <apex:actionRegion> so lets remove that and see what happens when i just click the button, without selecting any of the checkboxes first.. Oh shit it works... the values are null, but they should be since nothing is selected.. Ok lets try changing the <apex:selectCheckboxes> to <apex:selectList> and see if they work.. damn, it seems to work, the resultsString is getting updated, but the values are still showing as null in the logs so I guess its not working, but atleast its working better than the <apex:selectCheckboxes>.. 

It shouldn't be anything to do with the maps or repeat but why dont we do this the long way and see if that helps..

<apex:page showHeader="true" sidebar="true" controller="sfg_testBugWithActionButton">
<apex:form>
    <apex:outputpanel id="mainWrap">
      <div class="filterItem">
        <h2>Grade</h2>
        <apex:selectCheckboxes value="{!fGrade}" layout="pageDirection">
          <apex:selectOptions value="{!soGrade}" />
        </apex:selectCheckboxes>
      </div>
      <div class="filterItem">
        <h2>Gender</h2>
        <apex:selectCheckboxes value="{!fGender}" layout="pageDirection">
          <apex:selectOptions value="{!soGender}" />
        </apex:selectCheckboxes>
      </div>
      <div class="filterItem">
        <h2>Role</h2>
        <apex:selectCheckboxes value="{!fRole}" layout="pageDirection">
          <apex:selectOptions value="{!soRole}" />
        </apex:selectCheckboxes>
      </div>
      <apex:commandButton action="{!preformAction}" rerender="renderWrap" value="Submit Action" />
      <apex:outputpanel id="renderWrap">
        {!resultString}
      </apex:outputpanel>
    </apex:outputpanel>
  </apex:form>
</apex:page>


public class sfg_testBugWithActionButton {
  public String fGender {get; set;}
  public String fGrade {get; set;}
  public String fRole {get; set;}
  public List<SelectOption> soGender {get; set;}
  public List<SelectOption> soGrade {get; set;}
  public List<SelectOption> soRole {get; set;}
  public String resultString {get; set;}

  public sfg_testBugWithActionButton() {
    createfilterMap();
    resultString = 'on Load of page';
  }

  public PageReference preformAction() {
    system.debug('Gender: ' + fGender);
    system.debug('Grade: ' + fGrade);
    system.debug('Role: ' + fRole);
    resultString = 'button action preformed';
    return null;
  }

  private void createfilterMap() {
    soGender = new List<SelectOption>();
    soGender.add(new SelectOption('Male', 'Male'));
    soGender.add(new SelectOption('Female', 'Female'));
      
    soGrade = new List<SelectOption>();
    soGrade.add(new SelectOption('A', 'A'));
    soGrade.add(new SelectOption('B', 'B'));
    soGrade.add(new SelectOption('C', 'C'));
    
    soRole = new List<SelectOption>();
    soRole.add(new SelectOption('Support', 'Support'));
    soRole.add(new SelectOption('Sales', 'Sales'));
    soRole.add(new SelectOption('Marketing', 'Marketing'));
  }
}

No joy, same problem with or without the repeat when using <apex:selectCheckboxes>. Now lets try the same thing but with <apex:selectList> instead.. And it works! But why wouldn't it work when in the repeat or with the <apex:selectCheckboxes>? I also made sure it works when I added the attribute multiselect="true" to them, since that is what I would need anyhow if I had to use Lists over checkboxes.

So lets review: 
Right now it seems that anything with the <apex:selectCheckboxes> wont work (regardless of version since I did try bumping it back on both the page and controller). When trying to be dynamic and using a repeat to populate the Lists it fails as well for both <apex:selectCheckboxes> and <apex:selectList>. But if you do it the long way, you can get <apex:selectList> to work at the very least, although that means more fighting with style to get it to look like checkboxes if that is the desired effect. 

*sad panda*

I posted a simpler test of this not working here: Stackoverflow

**Update**
ANSWER FOUND and so not a bug lol bellow is the update made to the controller, and no update was needed to the page(see page at top of post for code):


public class sfg_testBugWithActionButton {

  public List<String> fGender {get; set;}
  public List<String> fGrade {get; set;}
  public List<String> fRole {get; set;}

  public String resultString {get; set;}

  public Map<String, List<SelectOption>> filterMap {get; set;}

  //this had to changed from Map<String, String> to what you see below
  public Map<String, List<String>> filterKeys {get; set;}

  public sfg_testBugWithActionButton() {
    //this was also added, and helped make it all work
    fGender = new List<String>();
    fGrade = new List<String>();
    fRole = new List<String>();

    // and so this got updated as well to use the param and not a string
    filterKeys = new Map<String, List<String>> {
      'Gender' => fGender,
      'Grade' => fGrade,
      'Role' => fRole
    };
    createfilterMap();
    resultString = 'on Load of page';
  }

  public PageReference preformAction() {
    resultString = 'button action preformed';
    return null;
  }

  private void createfilterMap() {
    filterMap = new Map<String, List<SelectOption>>();
    List<SelectOption> options = new List<SelectOption>();

    for (String s : filterKeys.keySet()) {
      if (s == 'Gender') {
        options = new List<SelectOption>();
        options.add(new SelectOption('Male', 'Male'));
        options.add(new SelectOption('Female', 'Female'));
        filterMap.put('Gender', options);
      }
      if (s == 'Grade') {
        options = new List<SelectOption>();
        options.add(new SelectOption('A', 'A'));
        options.add(new SelectOption('B', 'B'));
        options.add(new SelectOption('C', 'C'));
        filterMap.put('Grade', options);
      }
      if (s == 'Role') {
        options = new List<SelectOption>();
        options.add(new SelectOption('Support', 'Support'));
        options.add(new SelectOption('Sales', 'Sales'));
        options.add(new SelectOption('Marketing', 'Marketing'));
        filterMap.put('Role', options);
      } 
    }
  }
}


HAZA! :-)

No comments:

Post a Comment