MultiSelect Picklist In LWC Datatable Inline Edit

by Rijwan Mohmmed
multiselect-picklist-in-lwc-datatable-inline-edit-techdicer

Hello friends, today we are going to explore How to add MultiSelect Picklist In LWC Datatable Inline Edit. One common requirement is to add a multi-select picklist within a Datatable component to allow users to select multiple values from a list. After so many requests and comments now I developed MultiSelect Picklist In LWC Datatable.

Also, check this: Refresh Standard Related List in LWC

Key Highlights :

  1. We can select multiple options and save them directly in LWC Datatable.
  2. Easy to understand and customize.
  3. Use Multiselect Picklist Blog code.
  4. Create a MultiSelect-Picklist type field in Datatable.
  5. We can edit this field.
  6. We can show/hide this picklist by the pen icon.
  7. Picklist options fetch dynamically for LWC Datatable.

Code :

AccountDataController.cls: I create a Prospect__c multi-select picklist custom fields so before running the code create this. Values (Prospect,Customer, Pending)

public class AccountDataController {
     
    @AuraEnabled (cacheable=true)
    public static List<Account> fetchAccounts(){
        return [SELECT Id, Name, Type, Phone, Prospect__c
                FROM Account LIMIT 10];       
    }
}

First of all, we create a custom multi-select picklist LWC component. So we can use this in our LWC component later.

MultiSelectPickList.Html :

<template>

	<!-- Start Header Label Passed from Parent -->
	<template if:true={label}>
		<label class="slds-form-element__label">{label}</label>
	</template>
	<!-- End Header Label Passed from Parent -->
	<div class="slds-combobox_container">
		<div class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-is-open" aria-expanded="true"
			aria-haspopup="listbox" role="combobox" onmouseleave={handleMouseOut} onmouseenter={handleMouseIn}>
			<!-- Search Input -->
			<div class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right" role="none">
				<lightning-input disabled={disabled} class="inputBox" placeholder="Select an Option" onblur={handleBlur}
					onclick={showOptions} onkeyup={filterOptions} value={searchString} variant="label-hidden"
					id="combobox-id-1" data-id="tel"></lightning-input>
				<lightning-icon class="slds-input__icon" icon-name="utility:down" size="x-small"
					alternative-text="downicon"></lightning-icon>
			</div>
			<!-- Dropdown List -->
			<template if:true={showDropdown}>
				<div id="listbox-id-1" class={computedDropdownClass}>
					<ul class="slds-listbox slds-listbox_vertical recordListBox slds-dropdown_left slds-dropdown_length-with-icon-10 slds-dropdown_fluid"
						role="presentation">
						<template if:false={noResultMessage}>
							<template for:each={optionData} for:item="option">
								<li key={option.value} data-id={option.value} onmousedown={selectItem}
									class="slds-listbox__item eachItem" if:true={option.isVisible}>
									<template if:true={option.selected}>
										<lightning-icon icon-name="utility:check" size="x-small"
											alternative-text="icon"></lightning-icon>
									</template>
									<span class="slds-media slds-listbox__option_entity verticalAlign slds-truncate">{option.label}</span>
								</li>
							</template>
						</template>
						<template if:true={noResultMessage}>
							<li class="slds-listbox__item">
								<span class="slds-media slds-listbox__option_entity verticalAlign slds-truncate">{noResultMessage}</span>
							</li>
						</template>
					</ul>
				</div>
			</template>
		</div>
	</div>
	<!-- Multi Select Pills  -->
	<!--<template for:each={optionData} for:item="option">
		<template if:true={option.selected}>
			<lightning-pill label={option.label} key={option.value} name={option.value} onremove={closePill}>
				<lightning-icon icon-name="custom:custom11" alternative-text="Account"></lightning-icon>
			</lightning-pill>
		</template>
	</template>-->

</template>

MultiSelectPickList.Js :

import { LightningElement, track, api } from 'lwc';

export default class MultiSelectPickList extends LightningElement {

    @api options;
    @api selectedValue;
    @api selectedValues = [];
    @api label;
    @api disabled = false;
    @api multiSelect = false;
    @track value;
    @track values = [];
    @track optionData;
    @track searchString;
    @track noResultMessage;
    @track showDropdown = false;
    @api yaxis;

    @api connectedCallback() {
        this.showDropdown = false;
        var optionData = this.options ? (JSON.parse(JSON.stringify(this.options))) : null;
        var value = this.selectedValue ? (JSON.parse(JSON.stringify(this.selectedValue))) : null;
        var val = [];

        if (this.selectedValues) {
            val = (JSON.parse(JSON.stringify(this.selectedValues))).split(';');
        }

        var values = val;

        if (value || values) {
            var searchString;
            var count = 0;
            for (var i = 0; i < optionData.length; i++) {
                if (this.multiSelect) {
                    if (values.includes(optionData[i].value)) {
                        optionData[i].selected = true;
                        count++;
                    }
                } else {
                    if (optionData[i].value == value) {
                        searchString = optionData[i].label;
                    }
                }
            }
            if (this.multiSelect)
                this.searchString = count + ' Option(s) Selected';
            else
                this.searchString = searchString;
        }
        this.value = value;
        this.values = values;
        this.optionData = optionData;
    }


    renderedCallback() {
        this.template.querySelector("[data-id=tel]")?.focus();
    }

    @api clearAll() {
        var optionData = this.options ? (JSON.parse(JSON.stringify(this.options))) : null;
        for (var i = 0; i < optionData.length; i++) {
            if (this.multiSelect) {
                optionData[i].selected = false;
            }
        }

        this.searchString = 0 + ' Option(s) Selected';
        this.selectedValues = [];
        this.optionData = optionData;
    }

    filterOptions(event) {
        this.searchString = event.target.value;

        if (this.searchString && this.searchString.length > 0) {
            this.noResultMessage = '';
            if (this.searchString.length >= 2) {
                var flag = true;
                for (var i = 0; i < this.optionData.length; i++) {
                    if (this.optionData[i].label.toLowerCase().trim().startsWith(this.searchString.toLowerCase().trim())) {
                        this.optionData[i].isVisible = true;
                        flag = false;
                    } else {
                        this.optionData[i].isVisible = false;
                    }
                }
                if (flag) {
                    this.noResultMessage = "No results found for '" + this.searchString + "'";
                }
            }
            this.showDropdown = true;
        } else {
            this.showDropdown = false;
        }

        console.log(this.optionData);
    }

    selectItem(event) {
        var selectedVal = event.currentTarget.dataset.id;
        if (selectedVal) {
            var count = 0;
            var options = JSON.parse(JSON.stringify(this.optionData));
            for (var i = 0; i < options.length; i++) {
                if (options[i].value === selectedVal) {
                    if (this.multiSelect) {
                        if (this.values.includes(options[i].value)) {
                            this.values.splice(this.values.indexOf(options[i].value), 1);
                        } else {
                            this.values.push(options[i].value);
                        }
                        options[i].selected = options[i].selected ? false : true;
                    } else {
                        this.value = options[i].value;
                        this.searchString = options[i].label;
                    }
                }
                if (options[i].selected) {
                    count++;
                }
            }
            this.optionData = options;
            if (this.multiSelect) {
                this.searchString = count + ' Option(s) Selected';

                let ev = new CustomEvent('selectoption', { detail: this.values });
                this.dispatchEvent(ev);
            }


            if (!this.multiSelect) {
                let ev = new CustomEvent('selectoption', { detail: this.value });
                this.dispatchEvent(ev);
            }

            if (this.multiSelect)
                event.preventDefault();
            else
                this.showDropdown = false;
        }
    }

    showOptions() {
        if (this.disabled == false && this.options) {
            this.noResultMessage = '';
            this.searchString = '';
            var options = JSON.parse(JSON.stringify(this.optionData));
            for (var i = 0; i < options.length; i++) {
                options[i].isVisible = true;
            }
            if (options.length > 0) {
                this.showDropdown = true;
            }
            this.optionData = options;
        }
    }

    closePill(event) {
        var value = event.currentTarget.name;
        var count = 0;
        var options = JSON.parse(JSON.stringify(this.optionData));
        for (var i = 0; i < options.length; i++) {
            if (options[i].value === value) {
                options[i].selected = false;
                this.values.splice(this.values.indexOf(options[i].value), 1);
            }
            if (options[i].selected) {
                count++;
            }
        }
        this.optionData = options;
        if (this.multiSelect) {
            this.searchString = count + ' Option(s) Selected';

            let ev = new CustomEvent('selectoption', { detail: this.values });
            this.dispatchEvent(ev);
        }
    }

    handleBlur() {
        var previousLabel;
        var count = 0;

        for (var i = 0; i < this.optionData.length; i++) {
            if (this.optionData[i].value === this.value) {
                previousLabel = this.optionData[i].label;
            }
            if (this.optionData[i].selected) {
                count++;
            }
        }

        if (this.multiSelect) {
            this.searchString = count + ' Option(s) Selected';
        } else {
            this.searchString = previousLabel;
        }

        this.showDropdown = false;

        let ev = new CustomEvent('closepicklist', { detail: 'close' });
        this.dispatchEvent(ev);
    }

    handleMouseOut() {
        //this.showDropdown = false;
    }

    handleMouseIn() {
        //this.showDropdown = true;
    }

    get computedDropdownClass() {
        let axis = this.yaxis;
        axis = axis + 300;
        let directionCss = '';

        if (window.innerHeight < axis) {
            directionCss = 'showUpperDropDown';
        } else {
            directionCss = '';
        }

        let classs = 'slds-dropdown slds-dropdown_length-5 slds-dropdown_fluid ' + directionCss;
        return classs;

        /*if (this.showDropdown) {
            if (dropdownHeight === 'standard') {
                if (window.innerHeight <= VIEWPORT_HEIGHT_SMALL) {
                    dropdownLengthClass = 'slds-dropdown_length-with-icon-7';
                } else {
                    dropdownLengthClass = 'slds-dropdown_length-with-icon-10';
                }
            } else if (dropdownHeight === 'small') {
                dropdownLengthClass = 'slds-dropdown_length-with-icon-5';
            }
        }*/

        /*return classSet(
            `slds-listbox slds-listbox_vertical slds-dropdown slds-dropdown_fluid ${dropdownLengthClass}`
        )
            .add({
                'slds-dropdown_left':
                    alignment === 'left' || alignment === 'auto',
                'slds-dropdown_center': alignment === 'center',
                'slds-dropdown_right': alignment === 'right',
                'slds-dropdown_bottom': alignment === 'bottom-center',
                'slds-dropdown_bottom slds-dropdown_right slds-dropdown_bottom-right':
                    alignment === 'bottom-right',
                'slds-dropdown_bottom slds-dropdown_left slds-dropdown_bottom-left':
                    alignment === 'bottom-left'
            })
            .toString();*/
    }
}

MultiSelectPickList.css :

.verticalAlign {
 cursor: pointer;
    padding: 0px 5px !important;
}
.slds-dropdown {
    padding:0px !important;
}
.recordListBox {
 margin-top:0px !important;
 overflow-y: scroll;
}
.slds-listbox li {
    padding: .45rem 0.7rem !important;
    display: flex;
}
.inputBox input {
 padding-left: 10px;
}
.eachItem:hover {
    background-color: #F1F1F1;
    cursor: pointer;
}
  
/* For Scrolling */
::-webkit-scrollbar {
    width: 7px;
    height: 7px;
}
::-webkit-scrollbar-track {
   display: none !important;
}
::-webkit-scrollbar-thumb {
    border-radius: 10px;
    background: rgba(0,0,0,0.4);
}

.slds-dropdown_fluid{
	margin-top: 0px;
}

.showUpperDropDown{
    transform: translate3d(0px, -102px, 0px);
    top: 0px;
    left: 0px;
    will-change: transform;
}

Now we create a multi-select column component.

MultipicklistColumn.HTML :

<template>
	<div class="picklistSection" id="picklist">
		<div if:true={showPicklist} class="picklist-section">
			<c-multi-select-pick-list multi-select="true" onselectoption={handleSelectOptionList} options={options}
				selected-values={value} label="" onclosepicklist={closePicklist}>
			</c-multi-select-pick-list>
		</div>
		<div if:false={showPicklist} class="slds-table_edit_container slds-is-relative">
			<span class="slds-grid slds-grid_align-spread slds-cell-edit">
                <span class="slds-truncate" title={value}>{value}</span>
			<button data-id={context} class="slds-button slds-button_icon slds-cell-edit__button slds-m-left_x-small" tabindex="-1" title="Edit" name="tes"
                    onclick={handleClick} data-name="myButtonName" >
                    <svg class="slds-button__icon slds-button__icon_hint slds-button__icon_lock slds-button__icon_small slds-button__icon_edit slds-icon slds-icon-text-default slds-icon_xx-small"
                        aria-hidden="true">
                        <use xlink:href="/_slds/icons/utility-sprite/svg/symbols.svg?cache=9.37.1#edit"></use>
                    </svg>
                    <span class="slds-assistive-text">Edit</span>
                </button>
			</span>
		</div>
	</div>
</template>

MultipicklistColumn.JS: In this component, I used a static resource LWCDatatableMultiPicklist, Unzip and Upload this as a static resource and the name will be LWCDatatableMultiPicklist.

import { LightningElement, api, track } from 'lwc';
import { loadStyle } from 'lightning/platformResourceLoader';
import LWCDatatableMultiPicklist from '@salesforce/resourceUrl/LWCDatatableMultiPicklist';

export default class MultipicklistColumn extends LightningElement {
    @api label;
    @api placeholder;
    @api options;
    @api value;
    @api context;
    @track showPicklist = false;
    isRendered = false;
    @track yaxis;

    renderedCallback() {
        if (!this.isRendered) {
            Promise.all([
                loadStyle(this, LWCDatatableMultiPicklist),
            ]).then(() => { });
        }

        this.isRendered = true;
    }

    handleSelectOptionList(event) {
        //show the selected value on UI
        let picklistValues = '';

        if (event.detail) {
            picklistValues = event.detail.join(';');
        }

        console.log(picklistValues);
        this.value = picklistValues;

        //fire event to send context and selected value to the data table
        this.dispatchEvent(new CustomEvent('picklistchanged', {
            composed: true,
            bubbles: true,
            cancelable: true,
            detail: {
                data: { context: this.context, value: this.value }
            }
        }));
    }

    handleClick(event) {
        this.yaxis = event.clientY;
        this.showPicklist = true;
    }

    closePicklist() {
        this.showPicklist = false;
    }
}

MultipicklistColumn.CSS:

.picklist-section{
    margin-top: -1rem;
    margin-left: -0.5rem;
    position: absolute !important;
    min-width: 10%;
	z-index: 99999999999999999999999999;
}

.picklist-section .slds-dropdown{
    //position: fixed !important;
    max-height: 120px;
    max-width: fit-content;
    overflow: auto;
}

.slds-grid .slds-hyphenate {
    width: 100%
}

.slds-grid .slds-truncate {
    width: 100%
}

.slds-listbox{
    z-index: 99999999999999999999999999!important;
}

2. LWCCustomDatatableType Component: We create a custom type Datatable here which extends standard LWC Datatable. Also, create an extra HTML file multiselectpicklistColumn, so we can put the multiselect component here. Below is an image of the structure.

multiselectpicklistColumn.HTML :

<template>
        <c-multipicklist-column label={typeAttributes.label} value={typeAttributes.value}
		placeholder={typeAttributes.placeholder} options={typeAttributes.options} context={typeAttributes.context}>
	</c-multipicklist-column>
</template>

LWCCustomDatatableType.JS :

import LightningDatatable from 'lightning/datatable';
import multiselectpicklistColumn from './multiselectpicklistColumn.html';

export default class LWCCustomDatatableType extends LightningDatatable {
    static customTypes = {
        multiselectpicklistColumn: {
            template: multiselectpicklistColumn,
            editTemplate: multiselectpicklistColumn,
            standardCellLayout: true,
            typeAttributes: ['label', 'placeholder', 'options', 'value', 'context', 'variant','name']
        }
    };
}

Now Create Datatable with custom Types

lWCDatatableWithMultipicklist.HTML :

<template>
	<!-- create card -->
	<lightning-card variant="Narrow" title="Multiselect PickList In LWC Inline Datatable Edit"
		icon-name="standard:folder" class="cardSpinner">
		<!-- loader -->
		<div if:true={showSpinner}>
			<lightning-spinner alternative-text="Loading..." variant="brand">
			</lightning-spinner>
		</div>
		<!-----/loader-------->
		<div class="slds-var-p-around_small">
			<template if:true={data}>
				<c-l-w-c-custom-datatable-type key-field="Id" data={data} columns={columns}
					onpicklistchanged={multpicklistChanged} onvalueselect={handleSelection} draft-values={draftValues}
					oncellchange={handleCellChange} onsave={handleSave} oncancel={handleCancel}>
				</c-l-w-c-custom-datatable-type>
			</template>
		</div>
	</lightning-card>
</template>

lWCDatatableWithMultipicklist.JS:

import { LightningElement, track, wire } from 'lwc';
import fetchAccounts from '@salesforce/apex/AccountDataController.fetchAccounts';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import PROSPECT_FIELD from '@salesforce/schema/Account.Prospect__c';
import { updateRecord } from 'lightning/uiRecordApi';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { refreshApex } from '@salesforce/apex';
import { getPicklistValues, getObjectInfo } from 'lightning/uiObjectInfoApi';

const columns = [
    { label: 'Name', fieldName: 'Name', editable: true },
    { label: 'Phone', fieldName: 'Phone', type: 'phone', editable: true },
    {
        label: 'Prospect', fieldName: 'Prospect', type: 'multiselectpicklistColumn', editable: false, typeAttributes: {
            placeholder: 'Choose Type', options: { fieldName: 'pickListOptions' }, 
            value: { fieldName: 'Prospect__c' }, // default value for picklist,
            context: { fieldName: 'Id' } // binding account Id with context variable to be returned back
        }
    },
    
]

export default class LWCDatatableWithMultipicklist extends LightningElement {
    columns = columns;
    showSpinner = false;
    @track data = [];
    @track accountData;
    @track draftValues = [];
    lastSavedData = [];
    @track pickListOptions;
    isRenderd = false;

    @wire(getObjectInfo, { objectApiName: ACCOUNT_OBJECT })
    objectInfo;

    //fetch picklist options
    @wire(getPicklistValues, {
        recordTypeId: "$objectInfo.data.defaultRecordTypeId",
        fieldApiName: PROSPECT_FIELD
    })

    wirePickList({ error, data }) {
        if (data) {
            this.pickListOptions = data.values;
        } else if (error) {
            console.log(error);
        }
    }

    //here I pass picklist option so that this wire method call after above method
    @wire(fetchAccounts, { pickList: '$pickListOptions' })
    accountDataWe(result) {
        this.accountData = result;
        if (result.data) {
            this.data = JSON.parse(JSON.stringify(result.data));

            this.data.forEach(ele => {
                ele.pickListOptions = this.pickListOptions;
            })

            this.lastSavedData = JSON.parse(JSON.stringify(this.data));

        } else if (result.error) {
            this.data = undefined;
        }
    };

    updateDataValues(updateItem) {
        let copyData = JSON.parse(JSON.stringify(this.data));

        copyData.forEach(item => {
            if (item.Id === updateItem.Id) {
                for (let field in updateItem) {
                    item[field] = updateItem[field];
                }
            }
        });

        //write changes back to original data
        this.data = [...copyData];
    }

    updateDraftValues(updateItem) {
        let draftValueChanged = false;
        let copyDraftValues = [...this.draftValues];
        //store changed value to do operations
        //on save. This will enable inline editing &
        //show standard cancel & save button
        copyDraftValues.forEach(item => {
            if (item.Id === updateItem.Id) {
                for (let field in updateItem) {
                    item[field] = updateItem[field];
                }
                draftValueChanged = true;
            }
        });

        if (draftValueChanged) {
            this.draftValues = [...copyDraftValues];
        } else {
            this.draftValues = [...copyDraftValues, updateItem];
        }
    }

    //listener handler to get the context and data
    //updates datatable
    multpicklistChanged(event) {
        event.stopPropagation();
        let dataRecieved = event.detail.data;
        console.log(event.detail.data);

        let updatedItem = { Id: dataRecieved.context, Prospect__c: dataRecieved.value};
        console.log(updatedItem);
        this.updateDraftValues(updatedItem);
        this.updateDataValues(updatedItem);
    }

    //handler to handle cell changes & update values in draft values
    handleCellChange(event) {
        console.log(event.detail.draftValues[0]);
        let draftValues = event.detail.draftValues;
        draftValues.forEach(ele=>{
            this.updateDraftValues(ele);
        })
    }

    handleSave(event) {
        this.showSpinner = true;
        this.saveDraftValues = this.draftValues;

        const recordInputs = this.saveDraftValues.slice().map(draft => {
            const fields = Object.assign({}, draft);
            return { fields };
        });

        // Updateing the records using the UiRecordAPi
        const promises = recordInputs.map(recordInput => updateRecord(recordInput));
        Promise.all(promises).then(res => {
            this.showToast('Success', 'Records Updated Successfully!', 'success', 'dismissable');
            this.draftValues = [];
            return this.refresh();
        }).catch(error => {
            console.log(error);
            this.showToast('Error', 'An Error Occured!!', 'error', 'dismissable');
        }).finally(() => {
            this.draftValues = [];
            this.showSpinner = false;
        });
    }

    handleCancel(event) {
        //remove draftValues & revert data changes
        this.data = JSON.parse(JSON.stringify(this.lastSavedData));
        this.draftValues = [];
    }

    showToast(title, message, variant, mode) {
        const evt = new ShowToastEvent({
            title: title,
            message: message,
            variant: variant,
            mode: mode
        });
        this.dispatchEvent(evt);
    }

    // This function is used to refresh the table once data updated
    async refresh() {
        await refreshApex(this.accountData);
    }

    handleChange(event){
        console.log(event.detail);
    }
}

lWCDatatableWithMultipicklist.css :

.cardSpinner{
    position: relative;
}

lWCDatatableWithMultipicklist.js-meta.xml:

<?xml version="1.0"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
	<apiVersion>57.0</apiVersion>
	<isExposed>true</isExposed>
	<targets>
		<target>lightning__HomePage</target>
	</targets>
</LightningComponentBundle>

Output :

multiselect-picklist-in-lwc-datatable-inline-edit

Reference :

  1. LWC Datatable
  2. Inline Editing in lightning-datatable in LWC Salesforce
  3. Picklist in LWC Datatable Inline Edit
What’s your Reaction?
+1
37
+1
6
+1
1
+1
0
+1
12
+1
0

You may also like

10 comments

Kishan August 17, 2023 - 1:46 pm

I am not able to see edit svg icon in the LWC table

Reply
Michael October 3, 2023 - 5:02 pm

This works great, but the combobox options are cut off by the parent container. How can I ensure that, if there are several combobox options, they overflow and do not get cut off by the parent container?

Reply
Rijwan Mohmmed October 5, 2023 - 12:20 pm

You have to setup css in child component.

Reply
sfDev December 5, 2023 - 10:27 am

Hi, thanks for the solution. Could you please help with the data-inputable issue? Already set it to true in all possible places :), but still getting an error: Editable custom types must define an editTemplate that includes an element with attribute data-inputable set to “true”.

Reply
NV March 13, 2024 - 12:03 pm

Hi, did you get any solution for data-inputable true error?

Reply
Rijwan Mohmmed March 18, 2024 - 7:20 am

Still not

Reply
NV March 18, 2024 - 2:40 pm

Can you guide on the css then? in your video it looks perfect as out of the box, but I am getting both the edit icons. Could you please help?

Reply
Rijwan Mohmmed March 19, 2024 - 7:49 am

book a meeting https://topmate.io/rijwan_mohmmed, I will help you

Reply
Justin March 21, 2024 - 3:19 am

please add css here, whatever i tried still parent container is cutting off the conbobox dropdown

Reply
Vandana May 23, 2024 - 2:23 pm

Hi, I am facing [Cannot read properties of undefined (reading ‘valid’)]
processInlineEditFinish() error upon clicking save.

This is the underlying script (datatable.js)
function processInlineEditFinish(dt, reason, rowKeyValue, colKeyValue) {
const state = dt.state;
const inlineEditState = state.inlineEdit;
const shouldSaveData = reason !== ‘edit-canceled’ && !(inlineEditState.massEditEnabled && reason === ‘lost-focus’) && isValidCell(dt.state, rowKeyValue, colKeyValue);
if (shouldSaveData) {
const panel = dt.template.querySelector(IEDIT_PANEL_SELECTOR);
const editValue = panel.value;
const isValidEditValue = panel.validity.valid; (Here validity returns undefined)

Reply

Leave a Comment