Blog

Iterable Multi Select Picklist with Search Option in LWC

A Picklist is a widget that allows the user to select the data from the list of the option provided,

Here is the custom picklist with the search option which also allows the user to select the multiple option

Consider that there are two custom objects “Question” and “Answer”, both are in lookup relationship.

In the UI, the questions have to be displayed as a label and against each question, a picklist should display corresponding answer options. Users can either select a single option or multiple options for a question.

Question Object : 

Question Object

Answer Object :

Answer Object

Requirements :

Single Option :

Single Option

Multiple Option :

Multiple Option

Search Option :

Search Option

Apex Class :

public class multiplePickListClass { 
   @AuraEnabled
   public static List<question__c> question_ans(){
       List<question__c> question = new List<question__c>([SELECT Id, Name, question__c, (Select Id,option__c,value__c From answers__r order by option__c ASC) FROM question__c]);
       system.debug(question);
       return question;
   }
}

This code will return the list of array as below :

[
 {
    "Id": "a0C5j000004KjvYEAS",
    "Name": "one",
    "question__c": "What is the color of the ocean?",
    "answers__r": [
       {
          "question__c": "a0C5j000004KjvYEAS",
          "Id": "a0D5j0000073pXnEAI",
          "option__c": "A",
          "value__c": "Blue"
       },
       {
          "question__c": "a0C5j000004KjvYEAS",
          "Id": "a0D5j0000073pXiEAI",
          "option__c": "B",
          "value__c": "Red"
       },
       {
          "question__c": "a0C5j000004KjvYEAS",
          "Id": "a0D5j0000073pXdEAI",
          "option__c": "C",
          "value__c": "Orange"
       },
       {
          "question__c": "a0C5j000004KjvYEAS",
          "Id": "a0D5j0000073pXYEAY",
          "option__c": "D",
          "value__c": "Yellow"
       }
    ],
    "value": ""
 },
 {
    "Id": "a0C5j000004KjvOEAS",
    "Name": "Three",
    "question__c": "what are the arithmetic operators?",
    "answers__r": [
       {
          "question__c": "a0C5j000004KjvOEAS",
          "Id": "a0D5j0000073pYMEAY",
          "option__c": "A",
          "value__c": "Plus"
       },
       {
          "question__c": "a0C5j000004KjvOEAS",
          "Id": "a0D5j0000073pYREAY",
          "option__c": "B",
          "value__c": "Minus"
       },
       {
          "question__c": "a0C5j000004KjvOEAS",
          "Id": "a0D5j0000073pYHEAY",
          "option__c": "C",
          "value__c": "Multiplication"
       },
       {
          "question__c": "a0C5j000004KjvOEAS",
          "Id": "a0D5j0000073pYCEAY",
          "option__c": "D",
          "value__c": "Division"
       }
    ],
    "value": ""
 },
 ]

HTML CODE :

<template>
    <div class="slds-card slds-p-around_small">
        <template for:each="{totalPicklist}" for:item="pickValue">
            <div class="slds-grid slds-wrap slds-m-around_small" key="{pickValue.Id}" onblur="{handleCloseOption}">
                <div class="slds-form-element slds-size_2-of-12">
                    <label class="slds-form-element__label" for="combobox-id-2" id="combobox-label-id-30">{pickValue.question__c}</label>
                </div>
                <div class="slds-form-element__control slds-size_4-of-12">
                    <div class="slds-combobox_container slds-size_small">
                        <div class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-is-open">
                            <div class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right" role="none">
                                <input
                                    type="text"
                                    placeholder="{pickValue.placeHolder}"
                                    value="{pickValue.value}"
                                    data-pick-list-type="multiple"
                                    data-pick-final-fields="value__c"
                                    data-pick-list-iterate="totalPicklist"
                                    data-pick-final-value="finalResult"
                                    data-pick-iterate-option="answers__r"
                                    data-id="{pickValue.Id}"
                                    class="slds-input"
                                    onkeyup="{handlePicklistSearch}"
                                    onclick="{handlePicklistSearchFocus}"
                                />
                                <span class="slds-icon_container slds-icon-utility-down slds-input__icon slds-input__icon_right">
                                    <lightning-icon icon-name="utility:down" size="xx-small" data-id="{pickValue.Id}"></lightning-icon>
                                </span>
                            </div>
                            <template if:true="{pickValue.showOption}">
                                <div
                                    id="listbox-id-2"
                                    class="slds-dropdown slds-dropdown_length-5 slds-dropdown_fluid"
                                    data-pick-list-iterate="totalPicklist"
                                    data-pick-iterate-option="answers__r"
                                    data-pick-final-value="finalResult"
                                    role="listbox"
                                    onmouseleave="{handleCloseOption}"
                                >
                                    <ul class="slds-listbox slds-listbox_vertical" role="presentation">
                                        <template for:each="{pickValue.answers__r}" for:item="pickOption">
                                            <li
                                                role="presentation"
                                                class="slds-listbox__item"
                                                data-pick-list-type="multiple"
                                                data-pick-list-iterate="totalPicklist"
                                                data-pick-iterate-option="answers__r"
                                                data-pick-final-value="finalResult"
                                                data-pick-final-fields="value__c"
                                                key="{pickOption.Id}"
                                                data-pick-value="finalResult"
                                                data-id="{pickOption.Id}"
                                                onclick="{handleOptionSelected}"
                                            >
                                                <div id="option1" class="slds-media slds-listbox__option slds-listbox__option_plain slds-media_small" role="option">
                                                    <span class="slds-media__figure slds-listbox__option-icon">
                                                        <template if:true="{pickOption.selected}">
                                                            <lightning-icon class="yellow-icon" icon-name="utility:check" size="xx-small"></lightning-icon>
                                                        </template>
                                                    </span>
                                                    <span class="slds-media__body">
                                                        <span class="slds-truncate" title="Option A">{pickOption.option__c}-{pickOption.value__c}</span>
                                                    </span>
                                                </div>
                                            </li>
                                        </template>
                                    </ul>
                                </div>
                            </template>
                        </div>
                    </div>
                </div>
            </div>
        </template>
        <div class="slds-align_absolute-center">
            <button class="slds-button slds-m-top_small slds-button_destructive" data-pick-final-value="finalResult" data-pick-list-iterate="totalPicklist" onclick="{handleSubmit}">Submit</button>
        </div>
    </div>
</template>

Notes :

Usage of attributes used in HTML

  • data-pick-list-iterate="totalPicklist" ===> enter the variable name of the parent array
  • data-pick-final-value="finalResult". ===> enter the variable name where the final value to be saved as array
  • data-pick-iterate-option="Contacts" ====> enter the key name of the option in the parent array which act as options when clicked
  • data-pick-final-fields="LastName”. ====> enter the field name which need to be added as the final value when choosed.
  • data-pick-list-type="multiple". =====> The type can be specified as single or multiple, when specified as multiple, it allows the user to select multiple choices.

JS CODE :

import { LightningElement } from 'lwc';
import getStylesData from '@salesforce/apex/multiplePickListClass.question_ans';
export default class Multi_select_picklist extends LightningElement {
   optionValue   = [];
   totalPicklist = [];
   finalResult   = [];
  connectedCallback() {
     getStylesData().then(result => {
        this.totalPicklist = result;
        this.totalPicklist.map(list => list['value']=''); // need to include value key  in your parent array;
        console.log(JSON.stringify(this.totalPicklist,null,'\t'));
     }).catch(error => {
        console.log('Error', error);
     });
  }
  // function called when we focused on the search bar
  handlePicklistSearchFocus(event){
     let dataId                 = event.currentTarget.dataset.id; // get the id of account
     let pickListIterate        = event.currentTarget.dataset.pickListIterate; // to get the variable name of parent array
     let finalPickValue         = event.currentTarget.dataset.pickFinalValue; // to get the variable name where the final value saved
     let pickListIterateOption  = event.currentTarget.dataset.pickIterateOption; // to get the variable name of parent's options array
     let pickListType        = event.currentTarget.dataset.pickListType;
     let resultData   = Object.assign([],this[pickListIterate]);
     if(pickListIterate){
        resultData.map((list,index)=>{
           // create a new key value pair which holds the boolean for hide and show
           list.showOption   = list.Id == dataId ? true : list.showOption == true ? false : false ;
           list.filterOption =  list.filterOption ?  list.filterOption : list[pickListIterateOption]; // to create a dummy data for filter option
           this[finalPickValue].length < this[pickListIterate].length ? this[finalPickValue].push({id:list.Id,value:pickListType == 'multiple' ? [] : ''}) : this[finalPickValue];
           list.placeHolder  = list.placeHolder != 'Select Option' ? list.placeHolder != '' ? list.placeHolder : 'Select Option'  :  'Select Option';
           list.value = '';
          
        });
        this[pickListIterate] = Object.assign([],resultData);
     }
  }
   // function called when we typed on the search bar
  handlePicklistSearch(event){
     let dataId                 = event.currentTarget.dataset.id; // get the id of account
     let pickListIterate        = event.currentTarget.dataset.pickListIterate; // to get the variable name of parent array
     let finalPickValue         = event.currentTarget.dataset.pickFinalValue; // to get the variable name where the final value saved
     let filterRecord           = Object.assign([],this[pickListIterate]);
     let finalResultFields      = event.currentTarget.dataset.pickFinalFields;// to get the variable name where the final fields to save
     let pickListIterateOption  = event.currentTarget.dataset.pickIterateOption;// to get the variable name of parent's options array
     let dataValue              = event.currentTarget.value; // to get the Value typed
     console.log(finalResultFields);
     if(pickListIterate){
        let filterData       = filterRecord.find(list=> {return list.Id == dataId});
        let indexOfData      = filterRecord.indexOf(filterData);
        let filterOptionData = filterData.filterOption.filter((list)=>{
        return list[finalResultFields].replace(/\s/g,'').toLowerCase().includes(dataValue.replace(/\s/g,'').toLowerCase());
     });
        filterRecord[indexOfData]['value']  =  dataValue;
        filterRecord[indexOfData]['showOption']  = true;
        filterRecord[indexOfData][pickListIterateOption] = filterOptionData;
        this[pickListIterate]            = Object.assign([],filterRecord);
     }
  }
  // function called when we leave or moved from options template
  handleCloseOption(event){
     let pickListIterate     = event.currentTarget.dataset.pickListIterate;// to get the variable name of parent array
     let finalPickValue      = event.currentTarget.dataset.pickFinalValue;// to get the variable name where the final value saved
     let pickListIterateOption  = event.currentTarget.dataset.pickIterateOption;
     let resultData = Object.assign([], this[pickListIterate]);
     resultData.map((list,index)=>{list.showOption = false;
                                   list['selectedOptions']=this[finalPickValue][index]['value'];
                                   list['value'] = null;
                                   list[pickListIterateOption] = list['filterOption'];
                                   list['placeHolder']=this[finalPickValue][index]['value'].length > 0 ? list['placeHolder'] : 'Select Option' });
     this[pickListIterate]   = Object.assign([],resultData);
     console.log(resultData,pickListIterateOption);
  }
  // function called when we select the options
  handleOptionSelected(event){
     let dataId              = event.currentTarget.dataset.id; // get the id of account
     let pickListIterate     = event.currentTarget.dataset.pickListIterate;// to get the variable name of parent array
     let finalPickValue      = event.currentTarget.dataset.pickFinalValue;// to get the variable name where the final value saved
     let filterData          = this[pickListIterate].find(findData =>{return findData.showOption == true });
     let finalResultFields   = event.currentTarget.dataset.pickFinalFields;// to get the variable name where the final fields to save
     let indexOfData         = this[pickListIterate].indexOf(filterData);
     let pickListType        = event.currentTarget.dataset.pickListType;
     let pickListIterateOption  = event.currentTarget.dataset.pickIterateOption;
     if(pickListType == 'multiple'){
filterData.filterOption.map((list)=>{
              list.selected = list.Id == dataId ?
              (!list.selected ? true : false)  : list.selected  ;
              list.Id == dataId ?
                       this.handleFinalData(indexOfData,list[finalResultFields],list.selected,finalPickValue,pickListIterate)
                       : '';
             
     });
    let resultData = Object.assign([], this[pickListIterate]);
     resultData[indexOfData][pickListIterateOption] = filterData[pickListIterateOption];
     resultData[indexOfData].filterOption = filterData.filterOption;
     this[pickListIterate] = Object.assign([],resultData);
     }
     if(pickListType == 'single'){
   filterData.filterOption.map((list)=>{
     if(list.Id == dataId){
        console.log('select',list.selected);
      list.selected = (!list.selected ? true : false);
      console.log('select',list.selected);
      this[pickListIterate][indexOfData].placeHolder = list.selected ? list[finalResultFields] : '';
      this[finalPickValue][indexOfData].value = list.selected ? list[finalResultFields] : '';
     }else{
      list.selected = false;
     }
     });
     let resultData = Object.assign([], this[pickListIterate]);
     resultData[indexOfData][pickListIterateOption] = filterData[pickListIterateOption];
     resultData[indexOfData].filterOption = filterData.filterOption;
     this[pickListIterate] = Object.assign([],resultData);
     }
  }
  handleFinalData(dataId,value,remove,finalPickValue,pickListIterate){
     if(!remove){
     let filterData = this[finalPickValue][dataId].value.filter((list)=>{return list != value});
     this[finalPickValue][dataId].value = filterData;
     this[pickListIterate][dataId].placeHolder = JSON.stringify(filterData).replace(/[\])}[{(]/g, '');
     }else{
        this[finalPickValue][dataId].value.push(value);
        this[pickListIterate][dataId].placeHolder = JSON.stringify(this[finalPickValue][dataId].value).replace(/[\])}[{(]/g, '');
     }
  }
  handleSubmit(event){
     let finalPickValue      = event.currentTarget.dataset.pickFinalValue;
     let pickListIterate     = event.currentTarget.dataset.pickListIterate;
     let resultData = Object.assign([], this[pickListIterate]);
     resultData.map(list=>list.showOption = false);
     this[pickListIterate] = Object.assign([],resultData);
     console.log(this[finalPickValue]);
  }
}