I've left the explanation of wrapper classes, and the examples using one to fit your requirement, but I do have to wonder, why don't you use something like:
<apex:selectList value="{!a.Graduation_Requirement__c}" multiselect="false" size="1">
<apex:selectOptions value="{!gradReqs}"/>
</apex:selectList>
A wrapper class is usually a private inner class, which takes a record as in the constructor, and holds it there, with additional information. This allows you to use this extra data in your controller, or visualforce page.
As an example, lets say you are working on a simple page to select a number of records on this page. To do this without a wrapper class, you would need either a Map<Id, Boolean>
, or add another field on the object, such as Visualforce_Selected__c
. But what if you also want another field, such as a custom comment? You'll need another map, or another field, and so on.
To avoid this, you can add that extra data to the wrapper class. Below is a full code sample for this example, but lets focus on the important part here.
Each property can be added to this inner class, and will only exist on this data, which wraps a CustomObject__c
. You can see it takes a CustomObject__c
as a parameter to the constructor, and sets the property Selected
, and sets the wrapped record to the record provided.
private class InnerWrapperClass {
public CustomObject__c Record { get; set; }
public Boolean Selected { get; set; } // Example Property
public InnerWrapperClass(CustomObject__c record) {
this.Record = record;
this.Selected = false;
}
}
Using this class means you'll need to be specific about what data you want to operate on- you'll need to call someWrapperReference.Record.Some_Field__c
as opposed to someObjectReference.Some_Field__c
. You can see this in the visualforce snippet below:
<apex:repeat var="w" value="{!wrapperClass}">
<apex:input value="{!w.Selected}">
<apex:input value="{!w.Record.Name}">
</apex:repeat>
In the apex:repeat
, both the property on the wrapper class (Selected
), as well as a property on the wrapped sObejct (Record.Name
) are referenced.
Apex Class
public class YourController {
public List<InnerWrapperClass> wrapperClass { get; set; }
public YourController() {
wrapperClass = new List<InnerWrapperClass>();
// Add records if you have them
List<CustomObject__c> customObjects = [SELECT Id, Name FROM CustomObject__c LIMIT 5];
for (CustomObject__c c:customObjects) {
wrapperClass.add(new InnerWrapperClass(c));
}
}
public void Save() {
List<CustomObject__c> recordsToSave = new List<CustomObject__c>();
for (InnerWrapperClass w:wrapperClass) {
if (w.Selected) {
recordsToSave.add(w.Record);
}
}
update recordsToSave;
}
private class InnerWrapperClass {
public CustomObject__c Record { get; set; }
public Boolean Selected { get; set; } // Example Property
public InnerWrapperClass(CustomObject__c record) {
this.Record = record;
this.Selected = false;
}
}
}
Visualforce
<apex:repeat var="w" value="{!wrapperClass}">
<apex:input value="{!w.Selected}">
<apex:input value="{!w.Record.Name}">
</apex:repeat>
To make this example applicable to you, use a List<SelectOption>
inside your wrapper class, and provide the values in the constructor. It should look something like:
public InnerWrapperClass(List<SelectOption> options, SomeObject__c record) {
// ....
}
You'll need the properties:
public String SelectedOption { get; set; }
public List<SelectOptions> AvaliableOptions { get; set; }
public SomeObject__c Record { get; set; }
And your visualforce will look something like:
<apex:selectList value="{!wrapper.SelectedOption}" multiselect="false" size="1">
<apex:selectOptions value="{!wrapper.AvaliableOptions}"/>
</apex:selectList>
<apex:column headerValue="Id" value="{!wrapper.Record.Name}"/>
And lastly, you'll have to change your save method:
for(InnerWrapperClass w:Wrappers){
w.Record.Graduation_Requirement__c = w.SelectedOption;
}
Best Answer
While you can have some logic within repeat tags, you can't include literal Apex Code. You could, however, achieve this with either JavaScript (albeit clumsily), or by rendering this data with a wrapper class and some helper functions.
You'll see you have to use "formula-like" constructs, so it has some limitations (e.g. you can't get the a string into an array using pure Visualforce). A close approximation of what you could write might look like this:
However, you'll have to "wrap" your records into a wrapper class:
You add records to the wrapper like so:
As JavaScript, it'd probably be easier to just make a remote call for the data and then render it (perhaps by using jQuery). You could probably, however, expose the "queries" data simply by doing the following:
This might get tricky if you're trying to use reRenders and so on, but is certainly possible.