function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Genesis YauGenesis Yau 

Is it possible to change Map values from visualforce page?

I want to create a table that displays some time slots with a checkbox next to it. At the end of the table, there's a submit button. For the table, I used a Schedule__c object and also declared a Map, with the boolean being the default value for the checkbox.
This is the UI:
UI

This is a part of my VF code:
<apex:form id="results">
            <!-- <apex:outputText rendered="{!availableCourses.size == 0}" value="{!$Label.noResults}" /> -->
            <br/>
            <table style="width:100%">
                <thead>
                    <th>{!$Label.timeSlot}</th>
                    <th>{!$Label.courseStatus}</th>
                    <th>chose</th>
                </thead>
                <tbody>
                    <apex:repeat value="{!availableCourses}" var="result">
                        <apex:param value="{!courseOptions}"/>
                        <tr>
                            <td class="courseTime">
                                <apex:outputText value="{!result.Time__c}"></apex:outputText>
                            </td>
                            <td>test</td>
                            <td><apex:inputCheckbox value="{!courseOptions[result]}"/></td>
                        </tr>
                    </apex:repeat>
                </tbody>
            </table>
            <br/>
        </apex:form>
        <apex:form>
            <apex:commandButton value="{!$Label.submit}" action="{!Submit}" styleClass="btn btn-block" style="margin:0px;">

            </apex:commandButton>
        </apex:form>

This is part of my controller:
This is where I assign the default values to the Map, which has no problems at all.
for(ScheduleSetting__c a: availableCourses){
        courseOptions.put(a, false);
}

This is supposed to be my Submit function. It's not done yet because I'm trying to get the new values for the input checkboxes first.
 
public Pagereference Submit() {
    system.debug('aaa');

    for(ScheduleSetting__c key : courseOptions.keySet()){
        system.debug(courseOptions.get(key));
    }
    return null;
}
The problem is that even though I check the inputCheckBox when Submit gets called, the Map values don't change at all. I just want to be able to change the Map Boolean values depending on whether the checkbox is checked or not.
This is what I get in the Logs:
User-added image
 
Best Answer chosen by Genesis Yau
Alain CabonAlain Cabon
Don't use the reference of an object as a key even if it is authorized.

Uniqueness of map keys of user-defined types is determined by the equals and hashCode methods, which you provide in your classes. Uniqueness of keys of all other non-primitive types, such as sObject keys, is determined by comparing the objects’ field values.

If you change one of the value of the fields of the object used as a key, the equality test done internally is broken.
Account acc1 = new Account(name='test1');

Map<Account,Boolean> mac = new Map<Account,Boolean>();
mac.put(acc1,true);

system.debug(mac.get(acc1));  // true

acc1.name = 'test11';
​​​​​​​
system.debug(mac.get(acc1));   // null

I have used :  <td><apex:inputCheckbox value="{!courseOptions[result.myId]}"/></td>
 
for(Schedule__c a: availableTimeSlots) { 
  String tmpStr = GetStatus(a);
  waitingList.put(a.Id, tmpStr); 
  courseOptions.put(a.Id, false); 
}

or
Map<String,Schedule__c> myMap = new Map<String,Schedule__c>();
for(Schedule__c a: availableTimeSlots) {
    String tmpStr = GetStatus(a);
    String key = getRandom();    
    myMap.put(key,a);
    waitingList.put(key, tmpStr);
    courseOptions.put(key, false);
}

public Static String getRandom()
{
    return String.valueof(Math.round(Math.random()*1000000000));
}

 

All Answers

Alain CabonAlain Cabon
You don't need two forms and how do you declare : availableCourses  and courseOptions ?

public List<MyObject> availableCourses {get;set;}
public Map<String,Boolean> courseOptions {get;set;}
 
<apex:page controller="MapPage" >
    <apex:form id="results">     
        <br/>
        <table style="width:100%">
            <thead>
                <th>timeSlot</th>
                <th>courseStatus</th>
                <th>chose</th>
            </thead>
            <tbody>
                <apex:repeat value="{!availableCourses}" var="result">
                    <apex:param value="{!courseOptions}"/>
                    <tr>
                        <td class="courseTime">
                            <apex:outputText value="{!result.myTime}"></apex:outputText>
                        </td>
                        <td>test</td>
                        <td><apex:inputCheckbox value="{!courseOptions[result.myId]}"/></td>
                    </tr>
                </apex:repeat>
            </tbody>
        </table>
        <br/> 
        <apex:commandButton value="submit" action="{!save}" style="margin:10px;">
        </apex:commandButton>
    </apex:form>
</apex:page>

public class MapPage {
    public List<MyObject> availableCourses {get;set;}
    
    public Map<String,Boolean> courseOptions {get;set;}
    
    public MapPage()  {
        courseOptions = new  Map<String,Boolean>();
        availableCourses = new List<MyObject>();
        for (integer i=1;i <=10;i++) {
            MyObject obj1 = new MyObject();
            obj1.myTime = Date.today();
            obj1.myId = 'course' + i;
            availableCourses.add(obj1);
            courseOptions.put(obj1.myId,false);
        }        
    }
    
    public ApexPage save() {
        for (String key:courseOptions.keySet()) {
            system.debug('key:' + key + ' value:' + courseOptions.get(key));
        }
        return null;
    }
    
    class MyObject {
        public Date myTime{get;set;}    
        public String myId{get;set;}    
    }
    
}


 
Genesis YauGenesis Yau
Hi, Alain

Thanks a lot for your reply. I'm still unable to get my changed values.

This is how I declared my maps:
public Map<Schedule__c, Boolean> courseOptions{get; set;}
public Map<Schedule__c, String> waitingList{get; set;}
I ended up changing both maps to contain sObject instead.

I recently changed the form issue. However, I still can't change the boolean values from the courseOptions map.
courseOptions = new Map<Schedule__c, Boolean>();
waitingList = new Map<Schedule__c, String>();
This is how I populate both Maps:
for(Schedule__c a: availableTimeSlots) {
    String tmpStr = GetStatus(a);
    waitingList.put(a, tmpStr);
    courseOptions.put(a, false);
}
I changed the ln 18 of the VF page:
<apex:inputCheckbox id="check" value="{!courseOptions[result]}" immediate="true"></apex:inputCheckbox>
This is my save function (Still not complete since I can't still get the new values):
public ApexPage Save() {
        system.debug('aaa');
         
        // Loop that iterates through the map to check the checked inputcheckboxes in order to call the BookClass function
        for(Schedule__c key : courseOptions.keySet()) {
            if(courseOptions.get(key)) { // the boolean value
                system.debug('booking class');
                //BookClass(key, waitingList.get(key));
            }
            system.debug('[Submit] isChecked: ' + courseOptions.get(key));
        }
        return null;
    }


 
Alain CabonAlain Cabon
Don't use the reference of an object as a key even if it is authorized.

Uniqueness of map keys of user-defined types is determined by the equals and hashCode methods, which you provide in your classes. Uniqueness of keys of all other non-primitive types, such as sObject keys, is determined by comparing the objects’ field values.

If you change one of the value of the fields of the object used as a key, the equality test done internally is broken.
Account acc1 = new Account(name='test1');

Map<Account,Boolean> mac = new Map<Account,Boolean>();
mac.put(acc1,true);

system.debug(mac.get(acc1));  // true

acc1.name = 'test11';
​​​​​​​
system.debug(mac.get(acc1));   // null

I have used :  <td><apex:inputCheckbox value="{!courseOptions[result.myId]}"/></td>
 
for(Schedule__c a: availableTimeSlots) { 
  String tmpStr = GetStatus(a);
  waitingList.put(a.Id, tmpStr); 
  courseOptions.put(a.Id, false); 
}

or
Map<String,Schedule__c> myMap = new Map<String,Schedule__c>();
for(Schedule__c a: availableTimeSlots) {
    String tmpStr = GetStatus(a);
    String key = getRandom();    
    myMap.put(key,a);
    waitingList.put(key, tmpStr);
    courseOptions.put(key, false);
}

public Static String getRandom()
{
    return String.valueof(Math.round(Math.random()*1000000000));
}

 
This was selected as the best answer
Genesis YauGenesis Yau
Wow! I didn't think about the sObject keys problem. I've changed both maps to contain the sObject Id instead, but I still can't change the boolean values from the map. If the checkbox is checked, it should change its value to true.
 
<apex:outputPanel id="queryResults">
                   <br/>
                    <table style="width:100%">
                        <thead>
                            <th>Time slot</th>
                            <th>Status</th>
                            <th>Select</th>
                        </thead>
                        <tbody>
                            <apex:repeat value="{!availableCourses}" var="result">
                                <apex:param value="{!courseOptions}"/>
                                <apex:param value="{!waitingList}"/>
                                <tr style="display: {!IF(waitingList[result.Id] == $Label.cannotBookClass || waitingList[result.Id] == $Label.waitingListFull, 'none', 'table-row')};">
                                    <td class="courseTime">
                                        <apex:outputText value="{!result.Time__c}"></apex:outputText>
                                    </td>
                                    <td>
                                        <apex:outputText value="{!waitingList[result.Id]}"></apex:outputText>
                                    </td>
                                    <td>
                                        <apex:outputText id="test" value="{!courseOptions[result.Id]}"></apex:outputText>
                                        <apex:inputCheckbox value="{!courseOptions[result.Id]}">
                                        </apex:inputCheckbox>
                                    </td>
                                </tr>
                            </apex:repeat>
                        </tbody>
                    </table>
                    <br/>
                </apex:outputPanel>
public Pagereference Submit() {
        system.debug('aaa ');
         
        for (String key:courseOptions.keySet()) {
            //still prints false
            system.debug('key:' + key + ' value:' + courseOptions.get(key));
        }
        return null;
    }
for(Schedule__c a: availableTimeSlots) {
            String tmpStr = ComputeCourseStatus(a);
            waitingList.put(a.Id, tmpStr);
            courseOptions.put(a.Id, false);
}
Genesis YauGenesis Yau
I just solved my issue by adding an actionSupport for the inputCheckboxes. Thanks a lot, Alain.
<apex:inputCheckbox value="{!courseOptions[result.Id]}">
             <apex:actionSupport event="onclick"/>
</apex:inputCheckbox>