Use custom data types in lwc datatable to display picklist and toggel.

Shama Gurav
5 min readMar 7, 2024

--

Lightning-datatable can be used to display data in tabular format. Each column of the datatable renders data as per defined datatype in column definition. Lightning-datatable supports following standard datatypes.

|  action      |  boolean  |  button   | 
| button-icon | currency | date |
| date-local | email | location |
| number | percent | phone |
| text | url | |

however, if there is a requirement to display data in a format which is not supported by above listed data types then there is a provision given by salesforce of creating custom datatype of required formatting.

Defining and showing custom data types involves following steps,

  1. Create a lightning component which will extend the lightning data table
  2. Define your custom type in HTML format by creating template file inside the component created in step 1.
  3. Define custom type in .js controller file.

Code Example:

  1. Create lwc component to display toggel as below,

toggeltype.html

<template>
<div class="slds-form-element" >
<label class="slds-checkbox_toggle slds-grid">
<span class="slds-form-element__label slds-m-bottom_none"></span>
<input type="checkbox" name="checkboxtoggle"
value={togglevalue} checked={togglevalue} onchange={handleChange} />
<span id="checkboxtoggle" class="slds-checkbox_faux_container">
<span class="slds-checkbox_faux"></span>
<span class="slds-checkbox_on">Enabled</span>
<span class="slds-checkbox_off">Disabled</span>
</span>
</label>
</div>
</template>

Toggeltype.js

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

export default class Toggeltype extends LightningElement {

@api value;
@api context;
@track togglevalue;



renderedCallback()
{
this.togglevalue=this.value;
}

handleChange(event) {

event.preventDefault();

let value = event.target.checked;
this.value=value;
this.togglevalue=value;


const toggel = new CustomEvent('toggelselect', {
composed:true,
bubbles: true,
cancelable: true,
detail: {
data: { context: this.context, value: this.value }
}

});
this.dispatchEvent(toggel);


}
}

2. Create lightning component named ‘customDtTypeLwc’ and extend lighting data table. Final componnet structure should look like below,

customDtTypeLwc
-customDtTypeLwc.html
-customDtTypeLwc.js
-customDtTypeLwc.js-meta.js
-picklistNotEditable.html
-picklistEditable.html
-toggelTemplate.html

customDtTypeLwc.html

<template>

</template>

picklistNotEditable.html : this is the read only template for picklist custom dattype.

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

picklistEditable.html: this is the editable html template for picklist custom datatype. We are using lightning-combobox for displaying picklist values in this template.

<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>

toggelTemplate.html:

This is the template for toggel custom datatype. As we will be displaying lwc component for toggel, we are embeding lwc component toggeltype inside this template. Also, unlike picklist, there won’t be edit template for toggle because we are using lwc component.

<template>
<c-toggeltype value={typeAttributes.value} context={typeAttributes.context} ></c-toggeltype>
</template>

CustomDtTypeLwc.js :This is the js controller where custom datattypes i.e picklist and toggel are defined. Custom datatypes has following three attributes,

  1. template: HTML template which are defined for custom datatype. In this case toggelTemplate.html or picklistNotEditable.html.
  2. editTemplate: HTML template which needs to be rendered in edit mode. This attributes mainly used if you are using standard lighting component like ligtning combobox. if you are using custom lwc component like toggel type then edit template can be ignored.
  3. typeattributes: In this attribute you can pass data to data table.
import LightningDatatable from 'lightning/datatable';
import picklistEditable from './picklistEditable.html';
import picklistNotEditable from './picklistNotEditable.html';
import toggelTemplate from './toggelTemplate.html';

export default class CustomDtTypeLwc extends LightningDatatable {
static customTypes = {
picklistColumn: {
template: picklistNotEditable,
editTemplate: picklistEditable,
standardCellLayout: true,
typeAttributes : ['label', 'placeholder', 'options', 'value', 'context', 'variant','name']
},
toggel: {
template: toggelTemplate,
standardCellLayout: true,
typeAttributes : ['value', 'context']
}

};


}

3. Create a component to test the custom data type.

Create apex controller as bellow,

public with sharing class LWCInlineCtrl {
@AuraEnabled(Cacheable = true)
public static List<Contact> getContacts() {
return [SELECT Id, Name, FirstName, LastName, Phone, Email,GenderIdentity ,DoNotCall
FROM Contact
WHERE Email != null
AND Phone != null
ORDER BY CreatedDate limit 5];
}
}

Create new lightning Component “extended_inline_datatable”,

extended_inline_datatable.html

<template>

<lightning-card title=" Adding custom type picklist and toggel " icon-name="standard:contact">
<template if:true={contacts}>

<c-custom-dt-type-lwc
key-field="Id"
data={contacts}
columns={columns}
draft-values={saveDraftValues}
onsave={handleSave}
ontoggelselect={handletoggelselect}
oncellchange={handleCellChange}
oncancel={handleCancel}
hide-checkbox-column
show-row-number-column
>

</c-custom-dt-type-lwc>
</template>
</lightning-card>

</template>

extended_inline_datatable.js

import { LightningElement, wire, track } from 'lwc';
import getContacts from '@salesforce/apex/LWCInlineCtrl.getContacts';
import { updateRecord } from 'lightning/uiRecordApi';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { refreshApex } from '@salesforce/apex';
import { getPicklistValues, getObjectInfo } from 'lightning/uiObjectInfoApi';
import CONTACT_OBJECT from '@salesforce/schema/Contact';
import PICKLIST_FIELD from '@salesforce/schema/Contact.GenderIdentity';
import { RefreshEvent } from "lightning/refresh";

const columns = [
{
label: 'Name',
fieldName: 'Name',
type: 'text',
}, {
label: 'FirstName',
fieldName: 'FirstName',
type: 'text',
editable: true,
}, {
label: 'LastName',
fieldName: 'LastName',
type: 'text',
editable: true,
}, {
label: 'Phone',
fieldName: 'Phone',
type: 'phone',
editable: true,

},
{
label: 'Gender Identity', fieldName: 'GenderIdentity', type: 'picklistColumn', editable: true,
typeAttributes: {
placeholder: 'Choose Gender', options: { fieldName: 'pickListOptions' },
value: { fieldName: 'GenderIdentity' }, // default value for picklist,
context: { fieldName: 'Id' } // binding contact Id with context variable to be returned back
}
},
{
label: 'Do Not Call',
fieldName: 'DoNotCall',
type: 'toggel',
editable: false,
typeAttributes: {
value: { fieldName: 'DoNotCall' }, // default value for toggel,
context: { fieldName: 'Id' } // binding contact Id with context variable to be returned back
}
}
];

export default class Extended_inline_datatable extends LightningElement {
columns = columns;
@track contacts;
@track condata=[];
saveDraftValues = [];
@track pickListOptions;
lastSavedData=[];

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

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

wirePickList({ error, data }) {
if (data) {
this.pickListOptions = data.values;
} else if (error) {
console.log(error);
}
}
@wire(getContacts,{pickList: '$pickListOptions'})
contactData(result) {

if(result.data!=null && JSON.stringify(result.data)!='undefined')
{

this.contacts = JSON.parse(JSON.stringify(result.data));
this.lastSavedData=this.contacts;
this.contacts.forEach(ele => {
ele.pickListOptions=this.pickListOptions;
})
}


if (result.error) {
this.contacts = undefined;
}
};
//update list of contatcs with changed data
updateColumnData(updatedItem)
{
let copyData = JSON.parse(JSON.stringify(this.contacts));

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

this.contacts = [...copyData];
}
//update draft values to enable edit mode
updateDraftValuesAndData(updateItem) {
let draftValueChanged = false;
let copyDraftValues = [...this.saveDraftValues];

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

if (draftValueChanged) {
this.saveDraftValues = [...copyDraftValues];
} else {
this.saveDraftValues = [...copyDraftValues, updateItem];
}
console.log('draftValueChanged ' + draftValueChanged);
}

//if cell vlue is changed then update draft values and column list
handleCellChange(event) {


let draftValues = event.detail.draftValues;
draftValues.forEach(ele=>{
this.updateDraftValuesAndData(ele);
this.updateColumnData(ele);
})
}
//save the data
handleSave(event) {
this.saveDraftValues = event.detail.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.saveDraftValues = [];
return this.refresh();
}).catch(error => {
this.ShowToast('Error', 'An Error Occured!!', 'error', 'dismissable');
}).finally(() => {
this.saveDraftValues = [];
});
}
//handler for toggel select
handletoggelselect(event) {
console.log('in toggel select');
console.log(JSON.stringify(event));

event.stopPropagation();
let toggleid = event.detail.data.context;
let toggleValue = event.detail.data.value;
console.log('in toggel select toggleValue ' + toggleValue);
console.log(JSON.stringify(event));
let updatedItem = { Id: toggleid, DoNotCall: toggleValue };
this.updateDraftValuesAndData(updatedItem);
this.updateColumnData(updatedItem);


}
//Handel cancel
handleCancel(event) {
//remove draftValues & revert data changes
let savepicklist=this.pickListOptions;
this.contacts =[];
this.pickListOptions=null;
this.pickListOptions=savepicklist;

}

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.contacts);
}
}

Final output:

--

--

Responses (2)