How to create a multi-select picklist using lwc

15
4289

Hello #OhanaMembers,

I was working on a requirement where I had a requirement to implement the custom Multiselect combo box so that end users can select multiple options from the picklist.

We are not using the standard multi-select component as the length of the values is too big and the business users can not hover to each option to see which option they wanted to select.

So, As a Salesforce Architect, I have asked my team to built a reusable component and I am posting the same component here so that if anyone have such requirement in future can be get benefitted.

So, let’s start building the component. create a custom Lightning web component and use below code for JavaScript and HTML file.

<!--
@description :
@author : Amit Singh
@group :
@last modified on : 02-02-2021
@last modified by : Amit Singh
Modifications Log
Ver Date Author Modification
1.0 02-02-2021 Amit Singh Initial Version
-->
<template>
<div class="slds-form-element">
<label class="slds-form-element__label">{label}</label>
<div class="slds-form-element__control">
<div class="slds-combobox_container">
<div aria-expanded="false" aria-haspopup="listbox"
class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click" role="combobox">
<div class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right" role="none">
<input aria-controls="listbox-id-1" auto-complete="off" class="slds-input slds-combobox__input"
data-id="combobox-id-1" onclick={handleClick} role="textbox" value={inputValue} readonly
type="text" onblur={handleBlur}>
<div class="slds-input__icon-group slds-input__icon-group_right">
<lightning-icon class="slds-input__icon slds-input__icon_right" icon-name="utility:down"
size="xx-small"></lightning-icon>
</div>
</div>
<div class="slds-dropdown slds-dropdown_length-5 slds-dropdown_fluid" id="5"
onmouseenter={handleMouseEnter} onmouseleave={handleMouseleave} role="listbox">
<ul class="slds-listbox slds-listbox_vertical" role="presentation">
<template for:each={inputOptions} for:item="option">
<li class="slds-listbox__item" data-selected="false" data-id={option.value}
data-value={option.value} key={option.value} role="presentation"
onclick={handleSelection}>
<div class="slds-media slds-listbox__option slds-listbox__option_plain slds-media_small"
role="option">
<span class="slds-media__figure">
<lightning-icon
class="slds-icon-utility-check slds-current-color slds-listbox__icon-selected slds-icon_container"
icon-name="utility:check" size="x-small">
</lightning-icon>
</span>
<span class="slds-media__body">
<span class="slds-truncate" title={option.label}>{option.label}</span>
</span>
</div>
</li>
</template>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
/**
* @description :
* @author : Amit Singh
* @group :
* @last modified on : 02-02-2021
* @last modified by : Amit Singh
* Modifications Log
* Ver Date Author Modification
* 1.0 02-02-2021 Amit Singh Initial Version
**/
import {LightningElement, api, track} from 'lwc';
export default class MultiSelectPicklistLwc extends LightningElement {
/*
component receives the following params:
label - String with label name;
disabled - Boolean value, enable or disable Input;
options - Array of objects [{label:'option label', value: 'option value'},{...},...];
to clear the value call clear() function from parent:
let multiSelectPicklist = this.template.querySelector('c-multi-select-pick-list');
if (multiSelectPicklist) {
multiSelectPicklist.clear();
}
to get the value receive "valuechange" event in parent;
returned value is the array of strings - values of selected options;
example of usage:
<c-multi-select-pick-list options={marketAccessOptions}
onvaluechange={handleValueChange}
label="Market Access">
</c-multi-select-pick-list>
handleValueChange(event){
console.log(JSON.stringify(event.detail));
}
*/
@api label = "Default label";
_disabled = false;
@api
get disabled(){
return this._disabled;
}
set disabled(value){
this._disabled = value;
this.handleDisabled();
}
@track inputOptions;
@api
get options() {
return this.inputOptions.filter(option => option.value !== 'All');
}
set options(value) {
let options = [{
value: 'All',
label: 'All'
}];
this.inputOptions = options.concat(value);
}
@api
clear(){
this.handleAllOption();
}
value = [];
@track inputValue = 'All';
hasRendered;
renderedCallback() {
if (!this.hasRendered) {
// we coll the logic once, when page rendered first time
this.handleDisabled();
}
this.hasRendered = true;
}
handleDisabled(){
let input = this.template.querySelector("input");
if (input){
input.disabled = this.disabled;
}
}
comboboxIsRendered;
handleClick() {
let sldsCombobox = this.template.querySelector(".slds-combobox");
sldsCombobox.classList.toggle("slds-is-open");
if (!this.comboboxIsRendered){
let allOption = this.template.querySelector('[data-id="All"]');
allOption.firstChild.classList.add("slds-is-selected");
this.comboboxIsRendered = true;
}
}
handleSelection(event) {
let value = event.currentTarget.dataset.value;
if (value === 'All') {
this.handleAllOption();
}
else {
this.handleOption(event, value);
}
let input = this.template.querySelector("input");
input.focus();
this.sendValues();
}
sendValues(){
let values = [];
for (const valueObject of this.value) {
values.push(valueObject.value);
}
this.dispatchEvent(new CustomEvent("valuechange", {
detail: values
}));
}
handleAllOption(){
this.value = [];
this.inputValue = 'All';
let listBoxOptions = this.template.querySelectorAll('.slds-is-selected');
for (let option of listBoxOptions) {
option.classList.remove("slds-is-selected");
}
let allOption = this.template.querySelector('[data-id="All"]');
allOption.firstChild.classList.add("slds-is-selected");
this.closeDropbox();
}
handleOption(event, value){
let listBoxOption = event.currentTarget.firstChild;
if (listBoxOption.classList.contains("slds-is-selected")) {
this.value = this.value.filter(option => option.value !== value);
}
else {
let allOption = this.template.querySelector('[data-id="All"]');
allOption.firstChild.classList.remove("slds-is-selected");
let option = this.options.find(option => option.value === value);
this.value.push(option);
}
if (this.value.length > 1) {
this.inputValue = this.value.length + ' options selected';
}
else if (this.value.length === 1) {
this.inputValue = this.value[0].label;
}
else {
this.inputValue = 'All';
}
listBoxOption.classList.toggle("slds-is-selected");
}
dropDownInFocus = false;
handleBlur() {
if (!this.dropDownInFocus) {
this.closeDropbox();
}
}
handleMouseleave() {
this.dropDownInFocus = false;
}
handleMouseEnter() {
this.dropDownInFocus = true;
}
closeDropbox() {
let sldsCombobox = this.template.querySelector(".slds-combobox");
sldsCombobox.classList.remove("slds-is-open");
}
}

How to use

component receives the following params:

label – String with label name;        
disabled – Boolean value, enable or disable Input;       
options – Array of objects [{label:’option label’, value: ‘option value’},{…},…];

to clear the value call clear() function from parent:
        let multiSelectPicklist = this.template.querySelector('c-multi-select-pick-list');
        if (multiSelectPicklist) {
            multiSelectPicklist.clear();
        }

to get the value to receive “valuechange” event in the parent;        
the returned value is the array of strings – values of selected options;
an example of usage:

<c-multi-select-pick-list options={marketAccessOptions}
                                onvaluechange={handleValueChange}
                                label="Account Industry">
        </c-multi-select-pick-list>

        handleValueChange(event){
            console.log(JSON.stringify(event.detail));
        }

To see the demo, let’s create a new component that will use LDS to get the Account Industry and then pass those as an option to our component.

Demo

<!--
    @description       : 
    @author            : Amit Singh
    @group             : 
    @last modified on  : 02-02-2021
    @last modified by  : Amit Singh
    Modifications Log 
    Ver   Date         Author       Modification
    1.0   02-02-2021   Amit Singh   Initial Version
-->
<template>
    <lightning-card variant="Narrow" title="Mulit Select ComboBox" icon-name="custom:custom43">
        <div class="slds-p-horizontal_small">
            <c-multi-select-pick-list 
                if:true={picklistValues} 
                options={picklistValues}
                onvaluechange={handleValueChange} 
                label="Industry">
            </c-multi-select-pick-list>
        </div>
    </lightning-card>
</template>
/**
 * @description       : 
 * @author            : Amit Singh
 * @group             : 
 * @last modified on  : 02-02-2021
 * @last modified by  : Amit Singh
 * Modifications Log 
 * Ver   Date         Author       Modification
 * 1.0   02-02-2021   Amit Singh   Initial Version
**/
import { LightningElement, wire } from 'lwc';
import { getPicklistValues } from 'lightning/uiObjectInfoApi';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';

export default class LdsPicklist extends LightningElement {

    picklistValues;
    error;
    @wire(getPicklistValues, { 
        recordTypeId: '012000000000000AAA', fieldApiName: INDUSTRY_FIELD 
    })
    wiredPicklist({ error, data }){
        
        if(data){
            this.picklistValues = data.values;
            console.log(' data ', data.values);
            this.error = undefined;
        }
        if(error){
            this.picklistValues = undefined;
            this.error = error;
        }
    }

    handleValueChange(event){
        console.log(JSON.stringify(event.detail));
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>50.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
        <!--<target>lightning__Tab</target>-->
        <!--<target>lightning__Inbox</target>-->
        <!--<target>lightning__UtilityBar</target>-->
        <!--<target>lightning__FlowScreen</target>-->
        <!--<target>lightningSnapin__ChatMessage</target>-->
        <!--<target>lightningSnapin__Minimized</target>-->
        <!--<target>lightningSnapin__PreChat</target>-->
        <!--<target>lightningSnapin__ChatHeader</target>-->
        <!--<target>lightningCommunity__Page</target>-->
        <!--<target>lightningCommunity__Default</target>-->
    </targets>
</LightningComponentBundle>

Happy Learning 🙂

Please do, like, share and subscribe to receive more updates.

#DeveloperGeeks #AskPanther #SfdcPanther

15 COMMENTS

  1. Hi Amith,

    I have a requirement, when there is only one value coming to the picklist, i need to show that value as default value, not ‘All’. Could you please help me with code, i am new to LWC.

    • You have to send the value from parent component to child component and then push that value in the “value” arrays inside connectedCallback or renderedCallback.

      Unfortunately, I can not provide the code due to time constraints

  2. Hi , how could we set initial value for the picklist. For Eg: I need to set Industry to IT at initial load, how could I achieve that?

  3. Hi Amith,

    I am trying to pass selected values from multipicklist to apex but unable to do it , can you please let me know how to pass list to apex

  4. sir i want to have a multiselect combobox for the list of salesforce standard awa custom objects throguh LWC how to do that .while clicking on the drop down , pls help me on this

LEAVE A REPLY

Please enter your comment!
Please enter your name here