2

What is the best way to implement an apex:selectList add-on or equivalent, so that when the user selects "Other" a free-form text field appears to allow the user to enter any value (s)he wants?

2 Answers 2

1

I ended up implementing the picklist as three components, using jQuery for convenience. The idea is basically that an apex:inputHidden is actually used to store the value, with an apex:selectList used to render the picklist and an apex:inputText to render the dependent text box.

The three elements are "grouped" together by adopting an id attribute convention:

  • apex:selectList id="select"
  • apex:inputText id="selectText"
  • apex:inputHidden id="selectHidden"

Furthermore, the picklist is flagged for JavaScript initialization with by adding setting styleClass="with-other".

Below is an example of a component which uses this setup.

<apex:component >
    <!-- Attribute definitions -->
    <apex:attribute name="value" type="String"
                    description="The value to write to"/>

    <!-- UI components -->
    <apex:outputPanel id="selectPanel">
        <apex:selectList id="select" size="1" styleClass="with-other">
            <apex:selectOption itemLabel="--Select One--" itemValue=""></apex:selectOption>
            <apex:selectOption itemLabel="$10,000" itemValue="10000.00"></apex:selectOption>
            <apex:selectOption itemLabel="$100,000" itemValue="100000.00"></apex:selectOption>
            <apex:selectOption itemLabel="$1,000,000" itemValue="1000000.00"></apex:selectOption>
            <apex:selectOption itemLabel="Other" itemValue="Other"></apex:selectOption>
        </apex:selectList>
        <apex:inputText id="selectText" />
        <apex:inputHidden id="selectHidden" value="{!value}"/>
    </apex:outputPanel>
</apex:component>

Below is a page containing a proof of concept, demonstrating the use of the picklist and the dependent text input.

<apex:page docType="html-5.0" standardController="Account">
    <apex:includeScript value="{!URLFOR($Resource.jQueryScript)}"/>

    <apex:pageMessages/>

    <apex:form >
        Account Name: <apex:inputField value="{!Account.Name}"/><br/>
        Revenue: <c:selectListOther value="{!Account.AnnualRevenue}"/>
        <apex:commandButton action="{!save}" value="Save"/>
    </apex:form>

    <script>
    // Establish the Vfe namespace
    var Vfe = Vfe || {};

    Vfe.OTHER_OPTION_VALUE = 'Other';
    Vfe.SELECT_HIDDEN_ID_SUFFIX = 'Hidden';

    // The expected id attribute suffix for a
    // free-form input field that is used when a user
    // selects "Other" in a picklist
    Vfe.SELECT_TEXT_ID_SUFFIX = 'Text';

    Vfe.getSelectHiddenBySelectId = function(selectId) {
        var selectHiddenId = selectId + Vfe.SELECT_HIDDEN_ID_SUFFIX;
        return document.getElementById(selectHiddenId);
    };

    Vfe.getSelectTextBySelectId = function(selectId) {
        var selectTextId = selectId + Vfe.SELECT_TEXT_ID_SUFFIX;
        return document.getElementById(selectTextId);
    };

    // Define the utility function to set visibility of
    // a related "Other" element, given the element and the
    // condition
    Vfe.showSelectOther = function(select, condition) {
        var selectOther = Vfe.getSelectOther(select.id);
        $(selectOther).toggle(condition);
    };

    // Initialize the select.with-other elements
    $("select.with-other").each(function(index, element) {

        // Get references to the text input and the hidden input
        var selectText = Vfe.getSelectTextBySelectId(element.id);
        var selectHidden = Vfe.getSelectHiddenBySelectId(element.id);

        // Assume that the actual value is bound to the hidden input.
        // Look to see whether the hidden input value matches
        // anything in the select list.
        var isSelectable = false;
        for (var i = 0; i < element.options.length; i++) {
            var option = element.options[i];
            if (option.value == selectHidden.value)
                isSelectable = true;
        }

        // If the bound value is selectable, mark that value as selected.
        // Otherwise, assume the value was entered as an "Other".
        if (isSelectable) {
            element.value = selectHidden.value;
            selectText.value = null;
        } else {
            element.value = Vfe.OTHER_OPTION_VALUE;
            selectText.value = selectHidden.value;
        }

        // Bind event handlers to the picklist and the text input
        // so that values are written appropriately to the hidden input
        $(element).change(function(event) {
            if (event.target.value == Vfe.OTHER_OPTION_VALUE) {
                selectHidden.value = selectText.value;
                $(selectText).show();
            } else {
                selectHidden.value = event.target.value;
                $(selectText).hide();
            }
        });
        $(selectText).change(function(event) {
            selectHidden.value = event.target.value;
        });

        // Set the initial state for the text input
        $(selectText).toggle(element.value == Vfe.OTHER_OPTION_VALUE);
    });
    </script>
</apex:page>
0

Utilize an <apex:actionSupport event="onchange" rerender="thisPanel"/> inside your <apex:selectList>. Then, have in <apex:inputText rendered="{!SelectListValue = 'Other'}"/> also inside the rerendered panel so it would appear with the "Other" selection.

8
  • Thanks, Brad, I'm trying this now. By the way, do you mean apex:actionSupport and not apex:actionFunction?
    – Marty C.
    Commented Dec 10, 2014 at 20:11
  • Yes! way ahead of me. At one point I was thinking of using the action function in conjunction with onChange... but actionSupport should do the trick.
    – BradByte
    Commented Dec 10, 2014 at 20:14
  • Actually, I don't think this will work in my case, because I'm trying to write to a Currency field on an object through my custom component. When I bind my Currency field to the component, the Visualforce expression used in the rendered attribute causes an error, "Incorrect parameter type for operator '='. Expected Number, received Text". Here's my component: pastie.org/9772945
    – Marty C.
    Commented Dec 10, 2014 at 20:20
  • Could you try the <apex:input type="number"/>? I think it would require html-5 though.
    – BradByte
    Commented Dec 10, 2014 at 20:25
  • Does your component work without the "Other" addition?
    – BradByte
    Commented Dec 10, 2014 at 20:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .