I'll update this with an additional version that uses an actionFunction
in the way that it appears you're trying to use it... but wanted to start with this base.
Update: ActionFunction version added down below. It demonstrates the concept, but isn't 100% functional from a requirements perspective in the same way that the actionSupport version is primarily because of the lifecycle of the operations in this page.
Nothing fancy initially, just actionSupport
to provide the rerender onchange
event to the checkbox. Removed the styleClass
attributes written in a jQuery-like selector syntax as they don't directly contribute to the solution.
This page maintains the collection of all items (selected and non) in the page viewstate along with all of the other bits of data contained in the wrapper class instances.
Non-actionFunction version
VF markup:
<apex:page id="thePage" controller="CheckboxActionFunctionDemo">
<apex:form id="theForm">
<!-- Show messages to the user here -->
<apex:pageMessages />
<!-- Iterate the wrapper class instances and render each in a row-->
<apex:pageBlock id="thePageblock">
<apex:pageBlockTable id="data" value="{!ssOptions}" var="ssOpt">
<apex:column id="col1" >
<apex:outputLabel value="{!ssOpt.Option}" for="theCheckbox"></apex:outputLabel>
<apex:inputCheckbox id="theCheckbox" value="{!ssOpt.selected}" label="{!ssOpt.Option}">
<apex:actionSupport event="onchange" rerender="theForm" status="theStatus" />
</apex:inputCheckbox>
<apex:outputLabel value="{!ssOpt.Descr}" for="theTextArea" rendered="{!ssOpt.selected}"></apex:outputLabel>
<apex:inputTextarea id="theTextArea" label="{!ssOpt.Descr}" value="{!ssOpt.optionText}" rendered="{!ssOpt.selected}" />
</apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
<!-- command button does _not_ have immediate=true, which discards viewstate data in the post -->
<apex:commandButton value="Save Selected" action="{!processSelected}" rerender="theForm" status="theStatus"></apex:commandButton>
<!-- show something while we rerender -->
<apex:actionStatus startText="Working" id="theStatus" />
</apex:form>
</apex:page>
The controller:
public with sharing class CheckboxActionFunctionDemo {
public class ssOption {
public string boxId { get; set; }
public boolean selected { get; set; }
public string option { get; set; }
public string descr { get; set; }
public string optionText { get; set; }
}
public List<ssOption> ssOptions { get; set; }
public CheckboxActionFunctionDemo() {
// init the list
ssOptions = new List<ssOption>();
// populate the list
for (integer i = 1; i < 12; i++) {
ssOption opt = new ssOption();
opt.boxId = '' + i;
opt.selected = false;
opt.option = 'Option ' + i;
opt.descr = 'The description for option ' + i;
opt.optionText = '';
ssOptions.add(opt);
}
}
public void assignSelected() {
// don't really need to iterate the ssOptions collection
// and add the elements to a 'selected' list to achieve the behavior desired
// as long as all items in the ssOptions collection stay in viewstate
}
public void processSelected() {
try {
// Create a task for every selected thing
List<Task> ntsks = new List<Task>();
for (ssOption opt : ssOptions) {
if (opt.selected) {
Task t = new Task(Subject = opt.option, Description = opt.descr);
ntsks.add(t);
}
}
if (ntsks.size() > 0) {
insert ntsks;
// show the user the good news
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'Save was successful'));
}
} catch (Exception ex) {
// show the problem
ApexPages.addMessages(ex);
}
}
}
ActionFunction version
The actionfunction has 4 named parameters on it, these show up in the parameters collection in the controller just as URL params and all of the other form data. The parameter values from calling the actionfunction will be available there and don't necessarily need to be assigned to a controller member using the assignTo
syntax.
Data is passed to the actionfunction the same way you would with any JS function params. In this case 3 of them are strings populated with data from merge fields and the 4th is the checked
attribute of the checkbox DOM element.
onchange="assignSelectedJS('{!ssOpt.boxId}', this.checked, '{!ssOpt.Option}', '{!ssOpt.Descr}');"
The page:
<apex:page id="thePage" controller="CheckboxActionFunctionDemo2">
<apex:form id="theForm">
<apex:pageMessages />
<apex:pageBlock id="thePageblock">
<apex:pageBlockTable id="data" value="{!ssOptions}" var="ssOpt">
<apex:column id="col1" >
<apex:outputLabel value="{!ssOpt.Option}" for="theCheckbox"></apex:outputLabel>
<apex:inputCheckbox id="theCheckbox" value="{!ssOpt.selected}" label="{!ssOpt.Option}"
onchange="assignSelectedJS('{!ssOpt.boxId}', this.checked, '{!ssOpt.Option}', '{!ssOpt.Descr}');"></apex:inputCheckbox>
<apex:outputLabel value="{!ssOpt.Descr}" for="theTextArea" rendered="{!ssOpt.selected}"></apex:outputLabel>
<apex:inputTextarea id="theTextArea" label="{!ssOpt.Descr}" value="{!ssOpt.optionText}" rendered="{!ssOpt.selected}" />
</apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
<apex:commandButton value="Save Selected" action="{!processSelected}" rerender="theForm" status="theStatus"></apex:commandButton>
<apex:actionFunction name="assignSelectedJS" action="{!assignSelected}" rerender="theForm" status="theStatus">
<apex:param name="boxId" value="" />
<apex:param name="selected" value="" />
<apex:param name="Option" value="" />
<apex:param name="Description" value="" />
</apex:actionFunction>
<apex:actionStatus startText="Working" id="theStatus" />
</apex:form>
</apex:page>
The controller:
public with sharing class CheckboxActionFunctionDemo2 {
public class ssOption {
public string boxId { get; set; }
public boolean selected { get; set; }
public string option { get; set; }
public string descr { get; set; }
public string optionText { get; set; }
}
public List<ssOption> ssOptions { get; set; }
public CheckboxActionFunctionDemo2() {
// init the list
ssOptions = new List<ssOption>();
// populate the list
for (integer i = 1; i < 12; i++) {
ssOption opt = new ssOption();
opt.boxId = '' + i;
opt.selected = false;
opt.option = 'Option ' + i;
opt.descr = 'The description for option ' + i;
opt.optionText = '';
ssOptions.add(opt);
}
}
public void assignSelected() {
try {
// this map will contain all named params this page is aware of - including the 4 from the actionFunction
Map<String, Object> paramsMap = ApexPages.currentPage().getParameters();
// demonstrating the above claim
/*
for (String key : paramsMap.keyset()) {
system.debug('Key: ' + key + ' & Value: ' + paramsMap.get(key));
}
*/
// get the params that the actionFunction sent us
String boxId = (String)paramsMap.get('boxId');
Boolean selected = Boolean.valueOf(paramsMap.get('selected'));
String option = (String)paramsMap.get('Option');
String Description = (String)paramsMap.get('Description');
// the instance that we're going to update
ssOption theSSOption = null;
// this is terribly inefficient - don't do this loop, use a map instead.
for (ssOption opt : ssOptions) {
if (opt.boxId == boxId) {
theSSOption = opt; // found the match
break; // get out of the loop
}
}
if (theSSOption == null) {
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING, 'No matching id found.'));
return;
}
// update the instance member data
theSSOption.selected = selected;
theSSOption.option = option;
theSSOption.descr = Description;
// all done, rerender will fire and show hidden fields driven off of checkbox selected state
// show the user the good news
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'fields updated with data from actionfunction params'));
} catch (Exception ex) {
system.debug(ex);
ApexPages.addMessages(ex);
}
}
public void processSelected() {
try {
// do something with selected
List<Task> ntsks = new List<Task>();
for (ssOption opt : ssOptions) {
if (opt.selected) {
Task t = new Task(Subject = opt.option, Description = opt.descr);
ntsks.add(t);
}
}
if (ntsks.size() > 0) {
insert ntsks;
// show the user the good news
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'Save was successful'));
}
} catch (Exception ex) {
// show the problem
ApexPages.addMessages(ex);
}
}
}
onselect="assignSelectedJS(m.boxId,m.selected,m.Option,m.Descr)"
needs to use merge fields to pass VF values to actionFunction