lightning-dual-listbox/lightning combo multi select using LWC in Salesforce

lightning-dual-listbox/lightning combo multi select using LWC in Salesforce

Sample Code:

Apex Class:

public with sharing class OpportunityContactRoleController {
    
    @AuraEnabled(cacheable=true)
    public static OpportunityContactRoleWrapper fetchOpptyCons( String strRecordId ) {

        OpportunityContactRoleWrapper objWrap = new OpportunityContactRoleWrapper();
        List < ContactWrapper > availableCons = new List < ContactWrapper >();
        Set < String > selectedCons = new Set < String >();

        try {

            Opportunity oppty = [ SELECT Id, AccountId FROM Opportunity WHERE Id =: strRecordId ];
            Set < Id > setConIds = new Set < Id >();

            for ( OpportunityContactRole objOCR : [ SELECT Id, ContactId, Contact.Name FROM OpportunityContactRole WHERE OpportunityId =: strRecordId ] ) {

                ContactWrapper objCW = new ContactWrapper();
                objCW.value = objOCR.ContactId;
                objCW.label = objOCR.Contact.Name;
                availableCons.add( objCW );
                selectedCons.add( objOCR.ContactId );
                setConIds.add( objOCR.ContactId );

            }

            for ( Contact objCon : [ SELECT Id, Name FROM Contact WHERE AccountId =: oppty.AccountId ] ) {

                if ( !setConIds.contains( objCon.Id ) ) {

                    ContactWrapper objCW = new ContactWrapper();
                    objCW.value = objCon.Id;
                    objCW.label = objCon.Name;
                    availableCons.add( objCW );

                }

            }
            
        } catch ( Exception e ) {
            throw new AuraHandledException(e.getMessage());
        }
        
        objWrap.selectedCons = new List < String > ( selectedCons );
        objWrap.availableCons = availableCons;
        return objWrap;

    }

    @AuraEnabled
    public static String addRemoveOppCons( String strRecordId, List < String > selectedCons, List < String > updatedCons ) {

        try {

            Map < Id, Id > mapConIdOCRId = new Map < Id, Id >();
            List < OpportunityContactRole > listInsertOCR = new List < OpportunityContactRole >();
            Set < Id > setOCRIds = new Set < Id >();

            for ( OpportunityContactRole objOCR : [ SELECT Id, ContactId FROM OpportunityContactRole WHERE OpportunityId =: strRecordId ] ) {

                mapConIdOCRId.put( objOCR.ContactId, objOCR.Id );

            }

            for ( String strConId : updatedCons ) {

                if ( !selectedCons.contains( strConId ) ) {

                    listInsertOCR.add( new OpportunityContactRole( ContactId = strConId, OpportunityId = strRecordId ) );

                }

            }

            for ( String strConId : selectedCons ) {

                if ( !updatedCons.contains( strConId ) )
                    setOCRIds.add( mapConIdOCRId.get( strConId ) );

            }
            
            if ( listInsertOCR.size() > 0 ) {
             
                insert listInsertOCR;

            }

            if ( setOCRIds.size() > 0 ) {

                Database.delete( new List < Id > ( setOCRIds ), false );

            }

            return 'Successful';
            
        } catch (Exception e) {
            throw new AuraHandledException( e.getMessage() );
        }

    }

    public class OpportunityContactRoleWrapper {

        @AuraEnabled
        public List < ContactWrapper > availableCons;
        @AuraEnabled
        public List < String > selectedCons;

    }

    public class ContactWrapper {

        @AuraEnabled
        public String value;
        @AuraEnabled
        public String label;

    }

}

HTML:

<template>
    <lightning-card>
        <lightning-dual-listbox
            class="slds-box"
            name="OppCons"
            label="Select/Remove Contacts"
            source-label="Available Contact(s)"
            selected-label="Selected Contact(s)"
            options={availableCons}
            value={selectedCons}
            onchange={handleChange}>
        </lightning-dual-listbox>
        <p slot="footer">
            <lightning-button
                variant="brand"
                label="Save"
                onclick={saveChanges}            
                disabled={diableBool}>
            </lightning-button>
        </p>
    </lightning-card>        
</template>

JavaScript:

import { LightningElement, api, wire } from 'lwc';
import fetchOpptyCons from '@salesforce/apex/OpportunityContactRoleController.fetchOpptyCons';
import addRemoveOppCons from '@salesforce/apex/OpportunityContactRoleController.addRemoveOppCons';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class OpportunityContactRole extends LightningElement {

    @api recordId;
    availableCons;
    selectedCons;
    updatedCons;
    diableBool = true;

    @wire( fetchOpptyCons, { strRecordId: '$recordId' })  
    wiredRecs( { error, data } ) {

        if ( data ) {

            console.log( 'Records are ' + JSON.stringify( data ) );
            this.availableCons = data.availableCons;
            this.selectedCons = data.selectedCons;

        } else if ( error ) {

            console.log( 'Error ' + JSON.stringify( error ) );

        }
        
    }

    handleChange( event ) {

        this.diableBool = false;        
        const selectedOptionsList = event.detail.value;
        console.log( 'Selected Options are ' + JSON.stringify( selectedOptionsList ) );
        this.updatedCons = selectedOptionsList;

    }

    saveChanges() {

        this.diableBool = true;
        addRemoveOppCons( { strRecordId : this.recordId, selectedCons : this.selectedCons, updatedCons : this.updatedCons } )
            .then( result => {

                console.log( 'Result ' + JSON.stringify( result ) );
                let message;
                let variant;

                if ( result === 'Successful' ) {

                    message = 'Successfully Processed!';
                    variant = 'success';

                } else {

                    message = 'Some error occured. Please reach out to your Admin';
                    variant = 'error';
                    
                }

                const toastEvent = new ShowToastEvent({
                    title: 'Opportunity Contact Add/Remove',
                    message: message,
                    variant: variant
                });
                this.dispatchEvent( toastEvent );

            } )
            .catch( error => {
                console.log( 'Error ' + JSON.stringify( error ) );
            } );
        this.selectedCons = this.updatedCons;
            
    }
    
}

js-meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>51.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>

To test, add the LWC to the Opportunity Lightning Record Page.

Leave a Reply