function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
MellowRenMellowRen 

Using Javascript to update dependent picklist value

Hi

 

I wrote a visualforce page and what I thought was a cool chunk of javascript which, when executed, copies values in the page from some inputfields to others (without updating the record—user can still save or cancel). It works fine except in the case of two fields which are a controller/dependent picklist pair. Boiled right down, the problem code is here:

 

document.getElementById(DestContPListID).value = document.getElementById(SourceContPListID).value;
document.getElementById(DestDepdPListID).value = document.getElementById(SourceDepdPListID).value;

 

The first line populates the controller picklist correctly but despite the second line the dependent picklist does not gain a value (most of the time). The reason seems to be that after I set the Controller picklist value (eg Tea) the dependent picklist does not show the possible values (eg English Breakfast, Earl Grey, Oolong, etc) and even when the code attempts to set it to one of these allowed values the field remains blank.

 

Note, I have tried manually setting the Controller Picklist to the desired value first (ie Tea) and then the dependent displays the possible values. If I run the script afterwards, it works fine. I assume I need something like this:

 

document.getElementById(DestContPListID).value = document.getElementById(SourceContPListID).value;
document.getElementById(SourceContPListID).systemMethodWhichKicksTheStandardControllerIntoActionToUpdateElementsOnPage;
...

 Or may be not. 

 

BTW, I am using "pure" javascript. Haven't venture into using the AJAX Toolkit yet but willing to try if it is the solution here.

 

Thanks for any help.

 

Regards

MellowRen

 

Best Answer chosen by Admin (Salesforce Developers) 
MellowRenMellowRen

Over 24 hours of frustratingly trying everything I could think of before I finally found a working solution. Since I couldn’t find this on anywhere I thought I’d post it here in case it is useful to anyone else one day.

 

Using my boiled down version from above, I now split the javascript code into two functions and also use an Apex actionFunction element:

 

The JavaScript
--------------

function copyContPLValues (DestContPListID,SourceContPListID) {
    document.getElementById(DestContPListID).value = document.getElementById(SourceContPListID).value;
    copyAndRerenderDepdPLFields();
}

function copyDepdPLValues (DestDepdPListID,SourceDepdPListID) {
    document.getElementById(DestDepdPListID).value = document.getElementById(SourceDepdPListID).value;
}


The APEX Code
--------------

<apex:actionFunction name="copyAndRerenderDepdPLFields" oncomplete="return copyDepdPLValues('{!$Component.DestID}' ,'{!$Component.SourceID}');" rerender="DestID" />

 

If I don’t say so myself, that is quite an insane way to do things but it worked. The trick is that actionFunction appears to recalculate all the HTML elements of the rerender* before it calls the oncomplete function. This allows the possible values of the dependent picklist to be available which in turn allows the second function to successfully assign the value. Trying to force a rerender inside the javascript does not work—my guess is that sends off the "request" then continues down its merry way to the next line of code.

 

*This is not quite the same as saying it completes the rerender. There is a definate delay in the visual update.

 

All Answers

MellowRenMellowRen

Over 24 hours of frustratingly trying everything I could think of before I finally found a working solution. Since I couldn’t find this on anywhere I thought I’d post it here in case it is useful to anyone else one day.

 

Using my boiled down version from above, I now split the javascript code into two functions and also use an Apex actionFunction element:

 

The JavaScript
--------------

function copyContPLValues (DestContPListID,SourceContPListID) {
    document.getElementById(DestContPListID).value = document.getElementById(SourceContPListID).value;
    copyAndRerenderDepdPLFields();
}

function copyDepdPLValues (DestDepdPListID,SourceDepdPListID) {
    document.getElementById(DestDepdPListID).value = document.getElementById(SourceDepdPListID).value;
}


The APEX Code
--------------

<apex:actionFunction name="copyAndRerenderDepdPLFields" oncomplete="return copyDepdPLValues('{!$Component.DestID}' ,'{!$Component.SourceID}');" rerender="DestID" />

 

If I don’t say so myself, that is quite an insane way to do things but it worked. The trick is that actionFunction appears to recalculate all the HTML elements of the rerender* before it calls the oncomplete function. This allows the possible values of the dependent picklist to be available which in turn allows the second function to successfully assign the value. Trying to force a rerender inside the javascript does not work—my guess is that sends off the "request" then continues down its merry way to the next line of code.

 

*This is not quite the same as saying it completes the rerender. There is a definate delay in the visual update.

 

This was selected as the best answer
GennadiyGennadiy
Hi. It looks like my response is too late, but, maybe, it'll be useful for others.

I think SF adds specific logic in onchange handler for a controlling field. This logic manages values in a dependent picklist. At the same time, it's known that if you change value of select list in JavaScript, the onchange event does not fire  (this event occurs only when a user selects a new option in a browser).

Therefore, to solve this problem we need to call onchange event programmatically:
var controllingField = document.getElementById("controllingFieldId");
controllingField.value = "NewValue";
controllingField.onchange();

Another important note. When a user selects a new value in a controlling field, SF removes the current DOM element for a depedent field and creates a new one. So, if you read this DOM element before you call onchange() in a controlling field, store this element in a variable and then try to use it later, then you would find nothing changes in UI, because your variable refers to old DOM-element, which does not exist on your page anymore. So, you need to call document.getElementById("dependentFieldId"); after you call controllingField.onchange().
harsha__charsha__c
The "controllingField.onchange();" worked Great!

Thanks @Gennadiy

- Harsha
Mike ArthurMike Arthur
Note the very useful info above from Gennadiy, especially about the re-render.
In my case, I have 4 picklists, each dependent on the value of the one above.
The requirement that I needed to develop was that when a particular value is selected in the first one, certain values should be set in the second and third ones, and the appropriate options should be available in the 4th, given the value just set in the 3rd.
In an onchange handler for the first field, some simple code set the values for the second and third.  The change to the third value would just not show up though in the browser.
However, when I put it inside a setTimeout, it worked.  Something's going on under the covers, maybe some Visualforce framework stuff, that needs to clear the stack before setting the third value.
Then Gennadiy's tip helped to make the options available in the 4th picklist, otherwise it was inactive.
Hope the setTimeout() tip helps someone!
function firstFieldOnChangeHandler(thisObject) {
    if (thisObject.value === 'Special Value') {
        document.getElementById("page:feedback_form:second").value = 'Second Value';
        setTimeout(() => {
            document.getElementById("page:feedback_form:third").value = 'Third Value';
            document.getElementById("page:feedback_form:third").onchange();
       }, 0);
   } else {...