[SalesForce] Why does Visualforce ignore setting apex:selectList and apex:inputCheckbox values if manipulated (disabled) by jQuery

I have a VF Page where I wanted to alter an <apex:selectList value="{!dependentSelection}">'s options dynamically like a dependent picklist with another apex:selectList. I felt using apex:actionSupport took too long to update, so I manipulated the dependent apex:selectList's options with jQuery, but in doing so, Visualforce would no longer set dependentSelection in the controller.

I found a question, Apex:selectList value not getting set in controller, and the solution is to use an HTML and then set it's value to an <apex:hiddenInput value="{!dependentSelection}"/>.


Also in this VF Page, I denoted many selections with an <apex:inputCheckbox> and altered their checked and disabled properties so that only one selection per section could be chosen and that one selection is always selected:

Example:

<apex:repeat value="{!section.itemList}" var="item">
    <tr>
        <td><apex:inputCheckbox value="{!item.isSelected}" styleClass="selection" onclick="validateSelection(this);"/></td>
        <td><apex:outputField value="{!item.record.Label__c}"/></td>
    </tr>
</apex:repeat>

With JavaScript

var j$ = jQuery.noConflict;

// on load
j$('.selection:checked').prop('disabled', true);

function validateSelection(thar){
    j$('.selection').each(function(){
        if(this == thar)
            j$(this).prop('checked', true).prop('disabled', true);
        else
            j$(this).prop('checked', false).prop('disabled', false);
}

Using this technique worked fine for me in my sandbox, and when I tested this in production with my browser it also worked.

Weird Part # 1

I tried this same technique in a new VF Page (using the same API Version 31) but it did not work. The variables were mysteriously not getting set in the controller (all the inputCheckbox variables came back as false in the controller). The culprit turned out to be using the validateSelection jQuery function and the problem was the same as Apex:selectList value not getting set in controller so the solution was again using a hiddenInput.

The weird part was the old VF Page's validateSelection would still work fine without hidden inputs, but the new VF Page's validateSelection would not work without hidden inputs.

Weird Part #2

In testing, everything would work fine on the old VF Page for my boss using Chrome version 34, but it did not work for our Client using Chrome version 37. There is word that updating to Chrome (beta) version 38 does work for this technique on inputCheckboxes. Could this be a browser compatability issue? Testing on IE 11 works for my boss, but I still have run my own tests.


Now how can it work for me on one Page but not another?? I'm curious to what the reason is behind this (like what's going on behind the scenes with Visualforce) and if there is any hope that this technique may be supported in the future.

Currently, I believe it is necessary use the hiddenInput technique now and in the future, but please confirm this is necessary.

Also, I believe the hiddenInput technique is necessary only if JavaScript is altering an input's properties or child options. (Using j$(this).addClass('highlight'); causes no issues.)

Best Answer

Disabled form elements are not "successful controls" and the browser is not required to submit those with the form data. I'm not sure why your old page works, I suspect it's not doing exactly what you think it's doing but that's really beside the point.

Proper form state is key, if the data is to be submitted by the browser to the server it must be in a successful control. Controls which are disabled cannot be successful and therefore aren't posted.

W3.org 17.13 Form Submission

As an alternate solution, I would suggest that your script sets the readonly attribute instead of the disabled attribute.

W3.org 17.12.2 Read-only controls

var j$ = jQuery.noConflict;

// on load
j$('.selection:checked').prop('readonly', true);

function validateSelection(thar){
    j$('.selection').each(function(){
        if(this == thar)
            j$(this).prop('checked', true).prop('readonly', true);
        else
            j$(this).prop('checked', false).prop('readonly', false);
}
Related Topic