1

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.

3

Here is what that needs to be done to use the VF Component.

  1. 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;} 
    
  2. Initialize them in the Constructor

    selNames = new List<SelectOption>();
    items    = new List<SelectOption>();
    selected = new List<String>();
    
  3. In your execute where you are getting accountid, add the following

    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) {
        items.add(new SelectOption(s.name,s.name));
       }
    }
    else
       items.add(new SelectOption('None','None'));
    
  4. Finally when you select some values from left to right and click save, in the save method, add following to get the selected values.

       for(SelectOption so : selName){
           system.debug('Select vals ' + so.getValue());
           selected.add(so.getValue());
       }
    
  5. The Visualforce page SelectOption would be replaced with following

      <apex:pageBlockSection title="Subsidiaries" id="SelectedSubs">
      <apex:pageBlockSectionItem >  
        <c:MultiselectPicklist leftLabel="Available Subsidiaries"
            leftOptions="{!items}"
            rightLabel="Selected Subsidiaries"
            rightOptions="{!selNames}"
            size="14"
            width="150px"/>
        <apex:pageBlockButtons >
            <apex:commandButton value="Save" action="{!save}" rerender="selPanel"/>
        </apex:pageBlockButtons>
    </apex:pageBlockSectionItem> 
    <apex:pageBlockSectionItem 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
    

  • Thanks so much for this help. I am getting an error with the initialize part. It was saying at first that there was a token error, so I put a List<SelectOption> in front of them but that's giving me a "Duplicate variable" error. Is there another way to initialize them ? – Zoom_v Mar 26 '13 at 21:22
  • 1
    Not sure what you mean by token error, can you tell what error you are getting exactly? – logontokartik Mar 27 '13 at 12:27
  • I am currently having problems saving the Visual Force page. I have put the latest code at the top of my original post if you would like to see it. I have included the errors at the bottom. I also have a question regarding the Controller coding : 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 ? The way it is now, it looks like the field will not be properly populated without it, but I could be wrong. Thank you very much. I really appreciate the help and effort. – Zoom_v Mar 27 '13 at 19:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.