Picklist in LWC Datatable Inline Edit

by Rijwan Mohmmed
41 comments
picklist-in-lwc-datatable-inline-edit

Hello friends, today we are going to discuss How to set Picklist in LWC Datatable Inline Edit Salesforce. In LWC Datatable there are limited field types and the Picklist type is not there. But not to worry We will do this by creating a custom type Datatable and will create the Picklist type field.

Also, check this: Chain Wire methods in LWC

picklist-in-lwc-datatable-inline-edit-output-techdicer
picklist-in-lwc-datatable-inline-edit-output-techdicer

Key Highlights :

  1. Create a Picklist type field in Datatable
  2. We can edit this field
  3. We can show/hide this picklist by the pen icon.
  4. Update the data.
  5. Picklist options fetch dynamically for LWC Datatable.

Process & Code :

AccountDataController.cls :

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

lWCCustomDatatableType.HTML : We create a custom type Datatable here which extends standard LWC Datatable. Also, create an extra HTML file picklistColumn.html, pickliststatic.html so we can put the Picklist Combobox and its static value here. Below image of the structure

<template>

</template>

picklistColumn.html :

<template>
		<lightning-combobox name="picklist" data-inputable="true" label={typeAttributes.label} value={editedValue} placeholder={typeAttributes.placeholder} options={typeAttributes.options} variant='label-hidden'
            dropdown-alignment="auto"></lightning-combobox>
</template>

pickliststatic.html :

<template>
    <span class="slds-truncate" title={value}>{value}</span>
</template>

lWCCustomDatatableType.JS:

import LightningDatatable from 'lightning/datatable';
import picklistColumn from './picklistColumn.html';
import pickliststatic from './pickliststatic.html'

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

Now we will create the last final LWC component

lWCDatatableWithPicklist.html :

<template>
    <!-- header -->
    <div class="slds-tabs_card">
		<div class="slds-page-header">
			<div class="slds-page-header__row">
				<div class="slds-page-header__col-title">
					<div class="slds-media">
						<div class="slds-media__figure">
							<span class="slds-icon_container slds-icon-standard-opportunity">
								 <lightning-icon icon-name="standard:recipe" alternative-text="recipe" title="recipe"></lightning-icon>
                            </span>
						</div>
						<div class="slds-media__body">
							<div class="slds-page-header__name">
								<div class="slds-page-header__name-title">
									<h1>
										<span>PickList In LWC Inline Datatable Edit</span>
										<span class="slds-page-header__title slds-truncate" title="Recently Viewed">TechDicer</span>
									</h1>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div> <br/>
    <!-- /header -->

    <!-- create folder card -->
    <lightning-card  variant="Narrow"  title="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} 
                    onvalueselect={handleSelection}
                    draft-values={draftValues} 
                    oncellchange={handleCellChange}
                    onsave={handleSave}
                    oncancel={handleCancel}
                    hide-checkbox-column>
                </c-l-w-c-custom-datatable-type>
            </template>
        </div>
    </lightning-card>
</template>

lWCDatatableWithPicklist.JS:

import { LightningElement, track, wire } from 'lwc';
import fetchAccounts from '@salesforce/apex/AccountDataController.fetchAccounts';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import TYPE_FIELD from '@salesforce/schema/Account.Type';
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: 'Type', fieldName: 'Type', type: 'picklistColumn', editable: true, typeAttributes: {
            placeholder: 'Choose Type', options: { fieldName: 'pickListOptions' }, 
            value: { fieldName: 'Type' }, // default value for picklist,
            context: { fieldName: 'Id' } // binding account Id with context variable to be returned back
        }
    }
]

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

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

    //fetch picklist options
    @wire(getPicklistValues, {
        recordTypeId: "$objectInfo.data.defaultRecordTypeId",
        fieldApiName: TYPE_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' })
    accountData(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];
        }
    }

    //handler to handle cell changes & update values in draft values
    handleCellChange(event) {
        //this.updateDraftValues(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);
    }
}

lWCDatatableWithPicklist.css :

.cardSpinner{
    position: relative;;
}

Output :

picklist-in-lwc-datatable-inline-edit-output-techdicer
picklist-in-lwc-datatable-inline-edit-output-techdicer

Reference :

  1. LWC Datatable
  2. Inline Editing in lightning-datatable in LWC Salesforce

You may also like

41 comments

Mohsina October 20, 2022 - 5:33 am

Hii Rijwan the dropdown is not opening like that for me ,even when I used the same code.Can you please help

Reply
Rijwan Mohmmed October 23, 2022 - 5:46 am

Can you please share your file structure.

Reply
Marwan October 27, 2022 - 9:09 am

Hi Rijwan the dropdown works perfect for me when i put de LWC in a tab but it doesn’t when I put it on a record page, do you know why? Thank you!!

Reply
Rijwan Mohmmed October 28, 2022 - 3:47 pm

Can you please share a screen shot or video link.

Reply
Rijwan Mohmmed November 1, 2022 - 4:01 am

Also, check the position of the dropdown list.

Reply
Rijwan Mohmmed November 1, 2022 - 4:57 am

I have updated the CSS file which is the static resource LWCDatatablePicklist. Can you please check now.

Reply
Christian October 31, 2022 - 8:41 am

Hi Rijwan,
first thank for this code :). I have an error when i try to save new picklist value i receive
‘Field Type does not exist ‘

lwc002_EditInvoice.js:1 {Id: ‘a16Aa0000000PCxIAM’, Type: ‘Commit’}
t @ aura_prod.js:1
lwc002_EditInvoice.js:1 {status: 400, body: {…}, headers: {…}, ok: false, statusText: ‘Bad Request’, …}body: {message: ‘Field Type does not exist.’, statusCode: 400, errorCode: ‘POST_BODY_PARSE_ERROR’}errorType: “fetchResponse”headers: {}ok: falsestatus: 400statusText: “Bad Request”[[Prototype]]: Object

Could you help me

Reply
Rijwan Mohmmed November 1, 2022 - 3:24 am

check getPicklistValues , I think you are putting field which is not there in Org

Reply
Hassan November 2, 2022 - 5:02 pm

I tried your code, but options are undefined in the pickerColumn. I have checked wire is working correctly.

here is the column object
{
label: ‘Day Part’, fieldName: ‘label’, type: ‘picklistColumn’, editable: false, typeAttributes: {
placeholder: ‘Choose Type’,
options: { fieldName: ‘pickListOptions’ },
value: { fieldName: ‘label’ }, // default value for picklist,
context: { fieldName: ‘index’ } // binding account Id with context variable to be returned back
}
}

Reply
Rijwan Mohmmed November 3, 2022 - 12:54 pm

pickListOptions is getting values or not in JS

Reply
Oleh November 7, 2022 - 7:15 pm

Hi Rijwan. Thank you! Your code works perfectly for me. Data in all columns are edited and saved correctly. Except for picklist column. When I edit the picklist value, it doesn’t save(An error occured!!). What could be wrong?

Reply
Rijwan Mohmmed November 9, 2022 - 5:44 pm

What was the error?

Reply
Rijwan Mohmmed November 11, 2022 - 10:26 am

Can you please check Line no 108 in CustomDatatableDemo.JS

Reply
Oleh November 14, 2022 - 7:21 pm

When I change the picklist value and tried to save it, I got the nest error:
“Error! An error ocurred!!”.

Reply
Oleh November 17, 2022 - 4:49 am

You’re right. Thank you!

Reply
Duong November 18, 2022 - 10:18 am

can you show multi inline picklist with checked box?

Reply
Rijwan Mohmmed November 18, 2022 - 3:27 pm

yes we can use multi-select picklist.

Reply
Android November 30, 2022 - 11:53 am

data table save and cancel button at the bottom of datatable is not getting open when we change the value in combo box

Reply
Rijwan Mohmmed December 1, 2022 - 4:21 pm

Can you please send me a screenshot, As per my understanding it is working fine. If you have time I will post a new blog regarding this in simple steps at the end of this week.

Reply
saloni December 2, 2022 - 5:42 am

Hi Rijwan,

I able to load datatable but while clicking on picklist, Type picklist values is not loading.

Reply
Rijwan Mohmmed December 2, 2022 - 6:32 am

Hi Saloni,
please check below code and check console
//fetch picklist options
@wire(getPicklistValues, {
recordTypeId: “$objectInfo.data.defaultRecordTypeId”,
fieldApiName: TYPE_FIELD
})

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

Reply
MOHIT CHAUHAN December 8, 2022 - 1:26 pm

HI,
I want to use picklist for multiple columns.
Can you suggest how I can set picklistOptions for different columns.

Reply
Rijwan Mohmmed December 9, 2022 - 3:47 am

Hi MOHIT CHAUHAN
Just create other one only field type should be type: ‘picklistColumn’

Reply
MOHIT CHAUHAN December 9, 2022 - 5:37 am

I am unable to show different values in options for different picklist, because I am fetching columns dynamically from apex method with the help of metadata using field sets.

Reply
MOHIT CHAUHAN December 9, 2022 - 6:22 am

picklistColumns=YEAR_FIELD;
@wire (getMarketingPlanColumns, {currentMarketingPlanCountry: ‘$Country’})
marketingPlanColumns(result) {
if(result.data) {
this.columns = JSON.parse(JSON.stringify(result.data));
this.columns.push({
type: ‘action’,
typeAttributes:{rowActions: action}
});
this.columns.forEach(column => {
if(column.type == “PICKLIST”){
// @wire(getPicklistValues, {recordTypeId: ‘$recordTypeId’, fieldApiName: COUNTRY_FIELD})
// wirePickList({ error, data }) {
// if (data) {
// this.pickListOptions = data.values;
// console.log(this.pickListOptions);
// } else if (error) {
// console.log(‘error ‘+error);
// }
// };

column.typeAttributes = {
placeholder: ‘Choose Type’,
options: { fieldName: ‘pickListOptions’ },
value: { fieldName: ‘column.fieldName’ }, // default value for picklist,
context: { fieldName: ‘Id’ } // binding account Id with context variable to be returned back
}
}
})
// console.log(this.columns);
}
};

Reply
Rijwan Mohmmed December 9, 2022 - 2:39 pm

The issue in your JSON put type: ‘picklistColumn’ on columns where you want to the picklist. This is possible.

Balaji December 16, 2022 - 7:53 am

Hi Rijan

Not able to get picklist value in UI but it showing correcly in console.log

Reply
Balaji December 16, 2022 - 7:57 am

const columns = [
{ label: ‘Claim ID’, fieldName: ‘ClaimID__c’ },
{ label: ‘Member Name’, fieldName: ‘MH_MemberName__c’, type: ‘text’ },
{ label: ‘DOS From’, fieldName: ‘MH_DOSFrom__c’, type: ‘text’ },
{ label: ‘DOS To’, fieldName: ‘MH_DOSTo__c’, type: ‘text’ },
{ label: ‘Status’, fieldName: ‘MH_Status__c’, type: ‘text’ },
{label: ‘Denial Reason’, fieldName: ‘MH_Denial_Reason__c’, type: ‘picklistColumn’, editable: true, typeAttributes: {
placeholder: ‘Choose Type’, options: { fieldName: ‘pickListOptions’ },
value: { fieldName: ‘MH_Denial_Reason__c’ }, // default value for picklist,
context: { fieldName: ‘Id’ } // binding account Id with context variable to be returned back
}
}
];

export default class BasicDatatable extends LightningElement {
@api recordId;
@track recdata;
columns = columns;
@track pickListOptions;
draftValues = [];

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

@wire(getPicklistValues, {recordTypeId: ‘$objectInfo.data.defaultRecordTypeId’,fieldApiName: DENIAL_REASON })
getdenialreason({ error, data }) {
if (data) {
console.log(‘datapick’);
console.log(data);
this.pickListOptions = data.values;
console.log(‘this.pickListOptions’);
console.log(JSON.stringify(this.pickListOptions));

} else if (error) {
console.log(‘pickListOptionserror’);
console.log(error);
}
}

Reply
Rijwan Mohmmed December 16, 2022 - 12:30 pm

Hi @Balaji,
Have you tried my code with type picklist

Reply
Balaji December 16, 2022 - 2:18 pm

Yes but I have used your code LWCCustomDatatable and parent component is different

import { LightningElement,api,wire,track } from ‘lwc’;
import getdenialcashclaim from ‘@salesforce/apex/mh_caseclaimcontroller.getcashclaim’;
import { updateRecord } from ‘lightning/uiRecordApi’;
import { ShowToastEvent } from ‘lightning/platformShowToastEvent’;
import { refreshApex } from ‘@salesforce/apex’;
import { getPicklistValues, getObjectInfo } from ‘lightning/uiObjectInfoApi’;
import CASE_CLAIM from ‘@salesforce/schema/MH_CaseClaims__c’;
import DENIAL_REASON from ‘@salesforce/schema/MH_CaseClaims__c.MH_Denial_Reason__c’;
//import pickListValueDynamically from ‘@salesforce/apex/mh_caseclaimcontroller.pickListValueDynamically’;

const columns = [
{ label: ‘Claim ID’, fieldName: ‘ClaimID__c’ },
{ label: ‘Member Name’, fieldName: ‘MH_MemberName__c’, type: ‘text’ },
{ label: ‘DOS From’, fieldName: ‘MH_DOSFrom__c’, type: ‘text’ },
{ label: ‘DOS To’, fieldName: ‘MH_DOSTo__c’, type: ‘text’ },
{ label: ‘Status’, fieldName: ‘MH_Status__c’, type: ‘text’ },
{
label: ‘Denial Reason’, fieldName: ‘MH_Denial_Reason__c’, type: ‘picklistColumn’, editable: true, typeAttributes: {
label:{fieldName: ‘pickListOptions’},
placeholder: ‘Select Reason’, options: { fieldName: ‘pickListOptions’ },
value: { fieldName: ‘MH_Denial_Reason__c’ }, // default value for picklist,
context: { fieldName: ‘Id’ } // binding account Id with context variable to be returned back
}
}
];

export default class BasicDatatable extends LightningElement {
@api recordId;
@track recdata;
columns = columns;
@track pickListOptions;
draftValues = [];

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

@wire(getPicklistValues, {recordTypeId: ‘$objectInfo.data.defaultRecordTypeId’,fieldApiName: DENIAL_REASON })
getdenialreason({ error, data }) {
if (data) {
console.log(‘datapick’);
console.log(data);
this.pickListOptions = data.values;
console.log(‘this.pickListOptions’);
console.log(JSON.stringify(this.pickListOptions));

} else if (error) {
console.log(‘pickListOptionserror’);
console.log(error);
}
}

/* @wire(pickListValueDynamically, {customObjInfo: {‘sobjectType’ : ‘MH_CaseClaims__c’},
selectPicklistApi: ‘MH_Denial_Reason__c’})
getdenialreason({ error, data }) {
if (data) {
console.log(‘this.pickListOptions’);
console.log(data);
this.pickListOptions = data;

} else if (error) {
console.log(‘pickListOptionserror’);
console.log(error);
}
};*/

@wire(getdenialcashclaim,{recordId:’$recordId’})
getclaimrecord({error, data}){
if(data){
console.log(‘data’);
console.log(data);

this.recdata = data;
this.error = undefined;
}else{
console.log(‘error’);
this.error = error;
this.recdata = undefined;
}
}

}

*********************************************

Reply
Rijwan Mohmmed December 16, 2022 - 3:58 pm

Hi @Balaji, I think you missed something, Can you please check you HTML part .

Reply
Shiam Singh Rawat December 19, 2022 - 6:54 am

Hi Rijwan,
getting this error -> Invalid reference Order.Fsys_BillingType__c of type sobjectField in file fsys_PendingBillingReport.js: Source

Does your functionality work on custom fields??? I am getting this error on this line-> import OWNER_FIELD from ‘@salesforce/schema/Order.Fsys_BillingType__c’;
I was writing the code on Lightning studio(beta)

Reply
Rijwan Mohmmed December 19, 2022 - 11:47 am

HI Shiam Singh Rawat, This also work for custom objects

Reply
Rijwan Mohmmed December 19, 2022 - 11:48 am

Issue in Lightning studio(beta), you have to checked the checkbox in bottom one then deploy it will works

Reply
Jim December 30, 2022 - 5:20 pm

Hi Rijwan,

Thank you so much for paving the way here. I’ve tried to use your strategy in my component, but I’m getting an error in the console when I click on the pencil icon to edit the picklist field. “Cannot read properties of null (reading ‘value’).

And there is also another error: Editable custom types must define an editTemplate that includes an element with attribute data-inputable set to “true”.

Not sure if the two errors are related. I’ve checked my picklistColumn.html and it definitely has data-inputable=”true” as a property.

Any ideas?

Reply
Rijwan Mohmmed January 2, 2023 - 6:49 am

Hi @Jim,
Did you check all components deployed properly

Reply
Gopal January 5, 2023 - 11:36 am

Hi Rijwan
Can you tell me How to add upload file button in data table & how to add three picklist in single data table. Out of these three picklist one picklist is dependent picklist.

Reply
Rijwan Mohmmed January 5, 2023 - 12:03 pm

Hi Gopal, the upload file button can be added like row action add in LWC data table.
also, multiple picklists can be added but dependent picklists, no idea about this.

Reply
Gopal January 5, 2023 - 1:52 pm

Thank you Rijwan

Reply
Kari SaiMohan February 1, 2023 - 2:22 am

missing root template tag error in picklistColumn.html

Reply
Rijwan Mohmmed February 1, 2023 - 3:52 pm

check all components code again.

Reply

Leave a Comment