NEW CODE FOR VISUAL FORCE PAGE
<apex:pageBlockSection title="Subsidiaries" id="SelectedSubs">
<apex:pageBlockSectionItem >
<c:MultiselectPicklist leftLabel="Available Subsidiaries"
leftboxatt="{!items}"
rightLabel="Selected Subsidiaries"
rightboxatt="{!selNames}"
size="14"
width="150px"/>
<apex:pageBlockButtons >
<apex:commandButton value="Save" action="{!save}" rerender="selPanel"/>
</apex:pageBlockButtons>
</apex:pageBlockSectionItem >
</apex:pageBlockSection id="selPanel">
<apex:outputPanel>
<apex:outputLabel >Subsidiaries Selected:</apex:outputLabel>
<apex:repeat value="{!selected}" var="s">
<apex:outputtext value="{!s}" id="field"/>
</apex:repeat>
</apex:outputPanel>
Currently, I am getting this error :
The end-tag for element type "apex:pageblocksection" must end with a '>' delimiter
for this line :
</apex:pageBlockSection id="selPanel">
If I remove the id="selPanel" line then I get this error :
Duplicate ids have been detected: 'j_id0:j_id2:j_id3:SelectedSubs'
I know that I need the id=selPanel. I just wanted to try and save it without it to see if that was the only error I would get. I don't understand why that is giving an error because it seems like proper syntax.
Thank you very much for all of your help.
NEW CODE FOR CONTROLLER
public class MycontrollerSFDCBETA{
public Account selectedUser { get; set; }
public Contract_Overview__c contract{get;set;}
public string relatedAccount{get;set;}
public string names{get;set;}
public string accountid{get;set;}
public string selectedaccountid{get;set;}
public Name contractnew{get;set;}
String contractid;
**//NEW CODE - Add the following in your argument list
public List<SelectOption> selNames {get;set;}
public List<SelectOption> items {get;set;}
public List<String> selected {get;set;}
//END NEW CODE**
public MycontrollerSFDCBETA(apexpages.standardcontroller controller) {
//NEW CODE - Initialize in contructor
selNames = new List<SelectOption>();
items = new List<SelectOption>();
selected = new List<String>();
//END NEW CODE
contract = new Contract_Overview__c();
contractid=system.currentpagereference().getparameters().get('id');
if(contractid!=null) {
contract=[select account__c,Subsidiaries_On_Contract__c from Contract_Overview__c where id =:contractid];
relatedAccount=Contract.account__C;
}
}
public pageReference execute() {
accountid=contract.Account__c;
System.debug('########'+accountid);
return null;
}
public list<selectoption> getitems() {
List<selectoption> options= new list<selectoption>();
if(accountid != null) {
account a =[select name , (select name from Subsidiaries_and_Brands__r) from account where id =:accountid];
for(SubsidiariesAndBrands__c s : a.Subsidiaries_and_Brands__r) {
//NEW CODE - Change options to items
items.add(new SelectOption(s.name,s.name));
}
}
else
**//NEW CODE - Change options to items
items.add(new SelectOption('None','None'));**
**//NEW CHANGE this following return options; line is not in new code ; should it be taken out ?
return options;
}
**//NEW CODE - the following curly bracket not in original code
}**
public void save() {
System.debug('********************************' + names);
**//NEW CODE - added these lines to get selected values
for(SelectOption so : selNames) {
system.debug('Select vals ' + so.getValue());
selected.add(so.getValue());
}
//END NEW CODE**
contract.Subsidiaries_On_Contract__c=names;
insert contract;
contract=new Contract_Overview__c ();
System.debug('********************************' + contract);
}
}
Question : I'm confused with some of the coding. Shouldn't names be replaced with selNames in order for the rest of this code to work properly ?
contract.Subsidiaries_On_Contract__c=names;
insert contract;
contract=new Contract_Overview__c ();
System.debug('********************************' + contract);
I don't see where names will be used with the new additions ... ?
Thank you very much logontokartik. I really appreciate the help.
I have a SelectList whose options are created through a query based upon the value of another field. It is working great. However, I would like to now change it to a select dual-column picklist in the same style as when you add tabs in Salesforce. I found code which can do this which I would like to use. However, I am having problems converting this code to work with my current SelectList code.
Here is my current code which is running fine. Basically, it allows the user to select an Account. Once the Account is selected it runs a query and fills the selectlist options with the names of all the subsidiary accounts for the chosen account.
Essentially, I would like to adapt my current controller to work with the dual column picklist.
My Controller :
public class MycontrollerSFDC{
public Account selectedUser { get; set; }
public Contract_Overview__c contract{get;set;}
public string relatedAccount{get;set;}
public string names{get;set;}
public string accountid{get;set;}
public string selectedaccountid{get;set;}
public Name contractnew{get;set;}
String contractid;
public MycontrollerSFDC(apexpages.standardcontroller controller) {
contract = new Contract_Overview__c();
contractid=system.currentpagereference().getparameters().get('id');
if(contractid!=null) {
contract=[select account__c,Subsidiaries_On_Contract__c from Contract_Overview__c where id =:contractid];
relatedAccount=Contract.account__C;
}
}
public pageReference execute() {
accountid=contract.Account__c;
System.debug('########'+accountid);
return null;
}
public list<selectoption> getitems() {
List<selectoption> options= new list<selectoption>();
if(accountid != null) {
account a =[select name , (select name from Subsidiaries_and_Brands__r) from account where id =:accountid];
for(SubsidiariesAndBrands__c s : a.Subsidiaries_and_Brands__r) {
options.add(new SelectOption(s.name,s.name));
}
}
else
options.add(new SelectOption('None','None'));
return options;
}
public void save() {
System.debug('********************************' + names);
contract.Subsidiaries_On_Contract__c=names;
insert contract;
contract=new Contract_Overview__c ();
System.debug('********************************' + contract);
}
The VF page for the SelectList :
<apex:outputpanel id="SubsidiariesDetails">
<apex:pageblocksection title="Accounts and Subsidiaries" columns="2">
<apex:inputfield value="{!contract.Account__c}" required="false" >
<apex:actionSupport event="onchange" action="{!execute}"
rerender="SubsidiariesDetails,SelectedSubs" immediate="false" />
</apex:inputfield>
<apex:inputfield value="{!contract.Subsidiaries_On_Contract__c}" required="false"/>
</apex:pageblocksection>
<apex:pageBlockSection title="Subsidiaries" id="SelectedSubs">
<apex:pageBlockSectionItem >
<apex:outputLabel >Press Ctrl to select multiple subs:</apex:outputLabel>
<apex:selectList value="{!names}" multiselect="true" size="5">
<apex:actionSupport event="onclick" action="{!execute}"
reRender="field"/>
<apex:selectOptions value="{!items}"/>
</apex:selectList>
</apex:pageBlockSectionItem>
<apex:pageBlockSectionItem >
<apex:outputLabel >Subsidiaries Selected:</apex:outputLabel>
<apex:outputtext value="{!names}" id="field"/>
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
Here is the code which I have gotten which uses a dual-column picklist. It is much more complicated, using a component for the multi-select picklist :
Dual column select list controller:
/*
* MultiselectController synchronizes the values of the hidden elements to the
* SelectOption lists.
*/
public with sharing class MultiselectController {
// SelectOption lists for public consumption
public SelectOption[] leftOptions { get; set; }
public SelectOption[] rightOptions { get; set; }
// Parse &-separated values and labels from value and
// put them in option
private void setOptions(SelectOption[] options, String value) {
options.clear();
String[] parts = value.split('&');
for (Integer i=0; i<parts.size()/2; i++) {
options.add(new SelectOption(EncodingUtil.urlDecode(parts[i*2], 'UTF-8'),
EncodingUtil.urlDecode(parts[(i*2)+1], 'UTF-8')));
}
}
// Backing for hidden text field containing the options from the
// left list
public String leftOptionsHidden { get; set {
leftOptionsHidden = value;
setOptions(leftOptions, value);
}
}
// Backing for hidden text field containing the options from the
// right list
public String rightOptionsHidden { get; set {
rightOptionsHidden = value;
setOptions(rightOptions, value);
}
}
}
Dual column picklist Component :
<!--
The MultiselectPicklist component implements a multiselect picklist similar
to that seen when adding tabs to a Force.com application.
HTML elements use the same classes as the native multiselect picklist, to
keep visual consistency in the UI.
In addition to the visible elements, the component contains two hidden input
elements, the purpose of which is to hold a string representation of the
contents of each listbox. As options are added, removed or moved within the
listboxes, the content of the hidden elements is synchronized to the content
of the listboxes. When the Visualforce page is submitted, the
MultiselectController updates its SelectOption[] variables from these hidden
elements.
-->
<apex:component controller="MultiselectController">
<apex:attribute name="rightoptionsatt" type="String" description="TODO: Describe me"/>
<apex:attribute name="leftoptionsatt" type="String" description="TODO: Describe me"/>
<apex:attribute name="leftLabel" description="Label on left listbox."
type="String" required="true" />
<apex:attribute name="rightLabel" description="Label on right listbox."
type="String" required="true" />
<apex:attribute name="size" description="Size of listboxes."
type="Integer" required="true" />
<apex:attribute name="width" description="Width of listboxes."
type="String" required="true" />
<apex:attribute name="leftboxatt"
description="Options list for left listbox." type="SelectOption[]"
required="true" assignTo="{!leftOptions}" />
<apex:attribute name="rightboxatt"
description="Options list for right listbox." type="SelectOption[]"
required="true" assignTo="{!rightOptions}" />
<apex:outputPanel id="multiselectPanel" layout="block" styleClass="duelingListBox">
<table class="layout">
<tbody>
<tr>
<td class="selectCell">
<apex:outputPanel layout="block" styleClass="selectTitle">
<!--
Visualforce prepends the correct prefix to the outputLabel's
'for' attribute
-->
<apex:outputLabel value="{!leftLabel}"
for="multiselectPanel:leftList" />
</apex:outputPanel>
<select id="{!$Component.multiselectPanel}:leftList"
class="multilist" multiple="multiple" size="{!size}"
style="width: {!width};">
<apex:repeat value="{!leftOptions}" var="option">
<option value="{!option.value}">{!option.label}</option>
</apex:repeat>
</select>
</td>
<td class="buttonCell">
<apex:outputPanel layout="block" styleClass="text">Add</apex:outputPanel>
<apex:outputPanel layout="block" styleClass="text">
<apex:outputLink value="javascript:moveSelectedOptions('{!$Component.multiselectPanel}:leftList',
'{!$Component.multiselectPanel}:rightList', '{!$Component.leftHidden}',
'{!$Component.rightHidden}');"
id="btnRight">
<apex:image value="/s.gif" alt="Add" styleClass="rightArrowIcon"
title="Add" />
</apex:outputLink>
</apex:outputPanel>
<apex:outputPanel layout="block" styleClass="text">
<apex:outputLink value="javascript:moveSelectedOptions('{!$Component.multiselectPanel}:rightList',
'{!$Component.multiselectPanel}:leftList', '{!$Component.rightHidden}',
'{!$Component.leftHidden}');"
id="btnLeft">
<apex:image value="/s.gif" alt="Remove"
styleClass="leftArrowIcon" title="Remove" />
</apex:outputLink>
</apex:outputPanel>
<apex:outputPanel layout="block" styleClass="duelingText">Remove</apex:outputPanel>
</td>
<td class="selectCell">
<apex:outputPanel layout="block" styleClass="selectTitle">
<apex:outputLabel value="{!rightLabel}" for="multiselectPanel:rightList" />
</apex:outputPanel>
<select id="{!$Component.multiselectPanel}:rightList"
class="multilist" multiple="multiple" size="{!size}"
style="width: {!width};">
<apex:repeat value="{!rightOptions}" var="option">
<option value="{!option.value}">{!option.label}</option>
</apex:repeat>
</select>
</td>
<td class="buttonCell"><apex:outputPanel layout="block"
styleClass="text">Up</apex:outputPanel>
<apex:outputPanel layout="block" styleClass="text">
<apex:outputLink value="javascript:slideSelectedOptionsUp('{!$Component.multiselectPanel}:rightList',
'{!$Component.rightHidden}');"
id="upBtn">
<apex:image value="/s.gif" alt="Up" styleClass="upArrowIcon"
title="Up" />
</apex:outputLink>
</apex:outputPanel>
<apex:outputPanel layout="block" styleClass="text">
<apex:outputLink value="javascript:slideSelectedOptionsDown('{!$Component.multiselectPanel}:rightList',
'{!$Component.rightHidden}');"
id="downBtn">
<apex:image value="/s.gif" alt="Down" styleClass="downArrowIcon"
title="Down" />
</apex:outputLink>
</apex:outputPanel>
<apex:outputPanel layout="block" styleClass="text">Down</apex:outputPanel>
</td>
</tr>
</tbody>
</table>
<apex:inputHidden value="{!leftOptionsHidden}" id="leftHidden" />
<apex:inputHidden value="{!rightOptionsHidden}" id="rightHidden" />
</apex:outputPanel>
<script type="text/javascript">
if (!buildOutputString) {
// Create a string from the content of a listbox
var buildOutputString = function(listBox, hiddenInput) {
var str = '';
for ( var x = 0; x < listBox.options.length; x++) {
str += encodeURIComponent(listBox.options[x].value) + '&'
+ encodeURIComponent(listBox.options[x].text) + '&';
}
str.length--;
hiddenInput.value = str.slice(0, -1);
}
}
if (!moveSelectedOptions) {
// Move the selected options in the idFrom listbox to the idTo
// listbox, updating the corresponding strings in idHdnFrom and
// idHdnTo
var moveSelectedOptions = function(idFrom, idTo, idHdnFrom, idHdnTo) {
listFrom = document.getElementById(idFrom);
listTo = document.getElementById(idTo);
for ( var x = 0; x < listTo.options.length; x++) {
listTo.options[x].selected = false;
}
for ( var x = 0; x < listFrom.options.length; x++) {
if (listFrom.options[x].selected == true) {
listTo.appendChild(listFrom.options[x]);
x--;
}
}
listTo.focus();
buildOutputString(listFrom, document.getElementById(idHdnFrom));
buildOutputString(listTo, document.getElementById(idHdnTo));
}
}
if (!slideSelectedOptionsUp) {
// Slide the selected options in the idList listbox up by one position,
// updating the corresponding string in idHidden
var slideSelectedOptionsUp = function(idList, idHidden) {
listBox = document.getElementById(idList);
var len = listBox.options.length;
if (len > 0 && listBox.options[0].selected == true) {
return;
}
for ( var x = 1; x < len; x++) {
if (listBox.options[x].selected == true) {
listBox.insertBefore(listBox.options[x],
listBox.options[x - 1]);
}
}
listBox.focus();
buildOutputString(listBox, document.getElementById(idHidden));
}
}
if (!slideSelectedOptionsDown) {
// Slide the selected options in the idList listbox down by one position,
// updating the corresponding string in idHidden
var slideSelectedOptionsDown = function(idList, idHidden) {
listBox = document.getElementById(idList);
var len = listBox.options.length;
if (len > 0 && listBox.options[len - 1].selected == true) {
return;
}
for ( var x = listBox.options.length - 2; x >= 0; x--) {
if (listBox.options[x].selected == true) {
listBox.insertBefore(listBox.options[x + 1],
listBox.options[x]);
}
}
listBox.focus();
buildOutputString(listBox, document.getElementById(idHidden));
}
}
// initialize the string representations
buildOutputString(document.getElementById('{!$Component.multiselectPanel}:leftList'),
document.getElementById('{!$Component.leftHidden}'));
buildOutputString(document.getElementById('{!$Component.multiselectPanel}:rightList'),
document.getElementById('{!$Component.rightHidden}'));
</script>
</apex:component>
Dual Column VF Page :
<apex:page standardController="Contract_Overview__c" extensions="MultiselectExampleController">
<apex:form >
<apex:pageBlock title="Contacts">
<c:MultiselectPicklist leftLabel="Available Contacts"
leftboxatt="{!allContacts}"
rightLabel="Selected Contacts"
rightboxatt="{!selectedContacts}"
size="14"
width="150px"/>
<apex:pageBlockButtons >
<apex:commandButton value="Save" action="{!save}"/>
</apex:pageBlockButtons>
</apex:pageBlock>
</apex:form>
<apex:outputText >{!message}</apex:outputText>
</apex:page>
Most of it is really in the component, and I really don't think much has to be done w/ the component. It is essentially using two different string (leftoptions ; rightoptions) as the values of the two columns, but I'm not sure how my current code needs to be adapted to use them. I'm hoping to just use my controller with the component and just put the dual column picklist VF page into my current one.
btw : I got the original dual column picklist code from here :
A Multiselect Picklist Visualforce Component
I just wanted to give that person the proper credit.
Thank you very much for any help you can give here. I really appreciate it.