I have a highly dynamic aura controller working with some custom wrappers. The business case is we have a custom object that indicates a "case path" and each of those paths can have "required questions" that we push to a form for users to fill out as they escalate a case. These questions can be varying types, text, number, picklist, multi-select, etc. The output of the form is that it will create a record with some of those responses mapped directly to the record, but all responses must be mapped to a separate junction object on that new record and the required question object.

In my controller, there is a wrapper class defined for the required questions that contains list of another wrapper class to store the mutli-select picklist options.

public class RequiredInformation {
    @AuraEnabled public String reqInfoId{get;set;}
    @AuraEnabled public String comboId{get;set;}
    @AuraEnabled public String reqInfoResponse{get;set;}
    @AuraEnabled public List<String> options{get;set;}
    @AuraEnabled public List<MultiOptionsWrapper> multiOptions{get;set;}

    public RequiredInformation() {
        this.options = new List<String>();
        this.multiOptions = new List<MultiOptionsWrapper>();    

    public RequiredInformation(ComboRequired_Junction__c crj) { 
        this.options = new List<String>();
        this.multiOptions = new List<MultiOptionsWrapper>();  
        this.reqInfoResponse = '';

if(crj != null) {
if(String.isNotBlank(crj.Required_Information__r.Picklist_Values__c)) {
                    this.options = crj.Required_Information__r.Picklist_Values__c.split(';');

                for(String picklistVal : this.options)
                    this.multiOptions.add(new MultiOptionsWrapper(picklistVal));

public class MultiOptionsWrapper {
    @AuraEnabled public String value{get;set;}
    @AuraEnabled public String label{get;set;}

    public MultiOptionsWrapper() {

    public MultiOptionsWrapper(String picklistVal) {
        system.debug('multi options wrapper picklist constructor');         
        this.value = picklistVal;
        this.label = picklistVal;

The problem I'm running into, is I want to store the responses from an aura:iteration on the form directly onto this list to keep my responses coupled with my already queried information and Ids that I need to create the junction records. I've abbreviated the code for focus:

<aura:iteration items="{!v.requiredInfo}" var="ri" indexVar="i">
            <aura:if isTrue="{!ri.type == 'Multiselect Picklist'}">
                <lightning:dualListbox value="{!ri.reqInfoResponse}" sourceLabel="Available" selectedLabel="Chosen" options="{!ri.multiOptions}" onchange="{!c.handleInputChanges}"/>

The onchange event is just spitting out some output for me to view in the log, it's not doing anything to the data captured in this instance.

The problem I'm having is that I can see when I submit the form, the console is reporting a value, but the controller is getting stuck on the multi-select picklist response while loading the parameters.

Here's the action that calls the controller:

    //When the submit button is created, send all of the collected info to the controller and create the help ticket
submitHelpTicket : function(component) {    
    //set the parameters of the CasePathHelpTicketController.saveHelpTicket() function
var action = component.get('c.saveHelpTicket');         
    action.setParam('helpTicket', component.get('v.helpTicket'));
    action.setParam('accounts', component.get('v.accounts'));
    action.setParam('affectedTaxPeriods', component.get('v.affectedTaxPeriods'));
    action.setParam('reqInfos', component.get('v.requiredInfo'));

action.setCallback(this, function(response) { ...stuff here... }        



Here's the controller constructor:

    public static Response saveHelpTicket(Help_Ticket__c helpTicket, List<AccountWrapper> accounts, List<String> affectedTaxPeriods, List<RequiredInformation> reqInfos) { 
Savepoint sp = Database.setSavepoint();
        Response response = new Response(); //KS this might be it??
        List<CS_Case_Account__c> csCaseAccountsForInsert = new List<CS_Case_Account__c>();
        List<Affected_Tax_Period__c> affectedTaxPeriodsForInsert = new List<Affected_Tax_Period__c>();
        List<Case_Path__c> reqCaseInfoForInsert = new List<Case_Path__c>();

        try { ... inserting records }

Here's what I see in the console before it sends the parameters to the controller. ReqInfo[3] is the multi-select value coming off of {!ri.reqInfoResponse} prior to going to the apex controller. console values

Here's what I see in the debug log. The execution stops at the multi-select response just after loading the MultiSelectWrapper. There's no error in a full detail log, just an abrupt 'code finished.' debug log

I'm going to attempt to create another wrapper class to store the response and "copy" the relevant parts of the requiredInfo wrapper to get away from the need for the MultiSelectWrapper, but I feel like that approach is technically heavy and duplicative. Can someone explain why the approach I outlined above isn't working for multi-select picklists and recommend a more elegant way to store and pass the response over to the apex controller?

Here are the two additional methods I mentioned in comments for clarity. They do not perform actions on the dualList box outside of console logging.

handleInputChanges : function(component, event, helper) {
    //fieldName1 represents the scenario where the field type in the required info matched the field that matches on the HT's type
    var fieldName1 = event.getSource().get("v.fieldName");

    //fieldName2 represents the scenario where the field type in the required info differs from the field that matches on the HT's type
    var fieldName2 = event.getSource().get("v.name");       
    var fieldValue = event.getSource().get("v.value");

    if(fieldName1 != null) {
        console.log('*** handleInputChanges *** field updated(fieldName): ' + fieldName1 + ' *** field value: ' + fieldValue);      
    else if(fieldName2 != null) {
        console.log('*** handleInputChanges *** field updated(name): ' + fieldName2 + ' *** field value: ' + fieldValue);       
        component.set("v.helpTicket." + fieldName2, fieldValue);
    else {
        console.log('*** handleInputChanges *** value only updated: ' + fieldValue);

//Required Info Section Support
//additional support for multiselect picklists that are mapped to a help ticket field.
handleMultiPicklistChanges : function(component, event, helper) {       
    var fieldName = event.getSource().get("v.name");
    var multiPicklistVal = event.getSource().get("v.value");

    if(multiPicklistVal.includes(',')) {
        var multiResult = multiPicklistVal.replace(",", ";");
        component.set("v.helpTicket." + fieldName, multiResult);
        //console.log('*** handleMultiPicklistChanges *** field updated(multiResult): ' + fieldName2 + ' *** field value: ' + multiResult);
    else {
        component.set("v.helpTicket." + fieldName, multiPicklistVal);
        //console.log('*** handleMultiPicklistChanges *** field updated(multiPicklistVal): ' + fieldName2 + ' *** field value: ' + multiPicklistVal);
  • 1
    There's at least one problem in your code. aura:id must not be an expression, so it's possible that handleInputChanges isn't working the way you think it should, which might mean that you're passing in the wrong data, and thus causing Apex to not be able to parse the data correctly.
    – sfdcfox
    Nov 5, 2021 at 19:22
  • I'm adding two other methods handleInputChanges & handleMultiPicklistChanges. These functions support when the response needs to be mapped directly to a field on the help ticket object in addition to being created in the junction object. The msp works when there is a field mapping, but not when there's no mapping (ie response goes to reqInfo only). If there's no mapping, the only place the response is going is the value attribute listed on the input field. aura:id does accept the indexVar of aura:iteration shockingly. All other cases it does not allow field bindings. Nov 6, 2021 at 1:05
  • 1
    aura:id does not accept expressions (see documentation). Either you found a bug, or you're otherwise mistaken. Do not use an expression for aura:id. Aside from that, there's a lot of code here. Any way you might be able to trim this down?
    – sfdcfox
    Nov 6, 2021 at 2:24
  • I tried to trim it down some. The short of it is the multi-select response coming off of the dual list box isn't hitting the apex controller with values. The multi-select "field" is a wrapper within a wrapper. I have the response being posted back to the parent wrapper from the dualListbox value. This works fine when the question is bound to a HT field + reqInfo junction record. For whatever reason if it's not bound to a HT field, the execution just finishes when submit is clicked without reporting an error. The logs show that the last thing it did was load the multi-select wrapper. Nov 6, 2021 at 3:46
  • I figured it out! My hunch was right that it was an issue with the wrapper passing from the js to apex. I ended up having to dig and cobbled a couple solutions together: build a string from the wrapper array, then fetch the current reqInfo iteration, update the response attribute on that iteration and repush the component. I did take up your advice to ditch the aura:id (I was mistaken, lots of moving code) and replaced that section with a name attribute so I could fetch the index. I want give you some thanks for being so involved on this community - your responses have helped me many times. Nov 7, 2021 at 2:42

1 Answer 1


Answering my question - it was an issue with the dualListbox storing the response as an array instead of a concatenated string. The server side couldn't interpret it and it didn't throw an error.

I modified my dualListbox definition to use name instead of aura:id, removed my value param, and changed the onchange function. I removed the value param as I will be using the onchange function to "massage" the actual value to the string my wrapper class needs.

<lightning:dualListbox name="{!'noHT_multi' + i}" sourceLabel="Available" selectedLabel="Chosen" options="{!ri.multiOptions}" onchange="{!c.handleMultiPicklistChanges}"/>

Here's the updated onchange function. I had to iterate over the selected values for the multi-select list to build a new string, then fetch the current iteration, update it, and push it back.

handleMultiPicklistChanges : function(component, event, helper) {       
    var currReqInfoName = event.getSource().get("v.name");              
    var multiPicklistVal = event.getSource().get("v.value"); //this is a list because of the multi-select wrapper

    if(currReqInfoName.substr(0,5) == 'noHT_') {
        var index = currReqInfoName.substr(-1);
        var multiString = '';

        for(var i = 0; i < multiPicklistVal.length; i++) {
            if(multiString == '')
                multiString = multiPicklistVal[i];
                multiString = multiString + ';' + multiPicklistVal[i];

        var ri = component.get("v.requiredInfo")[index];
        ri.reqInfoResponse = multiString;
        component.set("v.requiredInfo["+index+"]", ri);
    //if currReqInfoName is a HT field, add the results to the field
    else {
        if(multiPicklistVal.includes(',')) {
            var multiResult = multiPicklistVal.replace(",", ";");
            component.set("v.helpTicket." + currReqInfoName, multiResult);
        else {
            component.set("v.helpTicket." + currReqInfoName, multiPicklistVal);             

Several helpful sources:

You must log in to answer this question.

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