1

I've created a custom LWC datatable and I am trying to add a custom picklist in one of the columns. However, console logs are showing the picklist options and values are undefined when loading the datatable. Any help would be greatly appreciated!

Screenshot of Table

Console Log Error

Datatable.JS

import { api, LightningElement, track, wire } from 'lwc';
import getUncategorizedOnbAppDocs from '@salesforce/apex/OnboardingFileLabelRemediationController.getUncategorizedOnbAppDocs';
import { updateRecord } from 'lightning/uiRecordApi';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { refreshApex } from '@salesforce/apex';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import { getPicklistValues } from 'lightning/uiObjectInfoApi';
import ONB_APP_DOC_OBJECT from '@salesforce/schema/onb_Onboarding_Application_Document__c';
import TYPE_FIELD from '@salesforce/schema/onb_Onboarding_Application_Document__c.Document_Type__c';

// columns
const columns = [
    {
        label: 'Onboarding Application Document Name',
        fieldName: 'Name',
        type: 'text',
        editable: true,
    }, {
        label: 'Document Type',
        fieldName: 'Document_Type__c',
        type: 'PicklistColumn',
        editable: false,
        wrapText: true,
            typeAttributes: {
                options: { fieldName : 'picklistOptions' },
                value: { fieldName : 'Document_Type__c'},
                placeholder: 'Choose a Document Type',
                context: { fieldName : 'Id'}
            }
    }, 
];

export default class OnboardingFileLabelRemediation extends LightningElement {
    columns = columns;
    showSpinner = false;
    data = [];
    onbAppDocs = [];
    draftValues = [];
    lastSavedData = [];
    pickListOptions = [];

    @wire(getObjectInfo, { objectApiName: ONB_APP_DOC_OBJECT })
    onbAppDocObjectMetadata;

    //Fetch picklist options
    @wire(getPicklistValues, { recordTypeId: '$onbAppDocObjectMetadata.data.defaultRecordTypeId', fieldApiName: TYPE_FIELD })
    onbAppDocTypePicklist ({ error, data }) {
        if (data) {
            this.pickListOptions = data.values;
            console.log('Line49:RESULT' + JSON.stringify(this.pickListOptions));
         } else if (error) {
            console.log(error);
        }
    }
    //Pass picklist options to onbAppDocs wire
    @wire(getUncategorizedOnbAppDocs, { pickList: '$pickListOptions' })
    onbAppDocs(result) {
        this.onbAppDocs = result;
        if (result.data) {
            this.data = JSON.parse(JSON.stringify(result.data));
            this.data.forEach(ele => {
                ele.pickListOptions = this.pickListOptions;
                console.log('LINE60:' + JSON.stringify(this.data));
            })
            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
    picklistChanged(event) {
        event.stopPropagation();
        let dataRecieved = event.detail.data;
        let updatedItem = { Id: dataRecieved.context, Type: dataRecieved.value };
        this.updateDraftValues(updatedItem);
        this.updateDataValues(updatedItem);
    }

    //handler to handle cell changes & update values in draft values
    handleCellChange(event) {
        this.updateDraftValues(event.detail.draftValues[0]);
    }

    handleSave(event) {
        this.showSpinner = true;
        this.saveDraftValues = this.draftValues;
        const recordInputs = this.saveDraftValues.slice().map(draft => {
            const fields = Object.assign({}, draft);
            return { fields };
        });
 
        // Updating 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.onbAppDocs);
    }
}

Datatable.HTML

<template>
    <lightning-card title="Uncategorized Onboarding Application Documents" icon-name="standard:document">
        <template if:true={data}></template>
        <c-l-w-c-custom-datatable-type
                    key-field="Id"
                    data={data} 
                    columns={columns} 
                    onpicklistchanged={picklistChanged} 
                    onvalueselect={handleSelection}
                    oncellchange={handleCellChange}
                    draft-values={draftValues}
                    onsave={handleSave}
                    oncancel={handleCancel} 
                    hide-checkbox-column
                    show-row-number-column>
        </c-l-w-c-custom-datatable-type>
   </lightning-card>
</template>

lWCCustomDatatableType.JS (Custom Type LWC)

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

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

pickistColumn.HTML (Custom Type LWC)

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

picklistColumn.html (Combobox LWC)

 <template>
    <div class="picklistSection" id="picklist">
        <div if:true={showPicklist} class="picklist-section">
            <lightning-combobox
            name="picklist" 
            label={label} 
            value={value} 
            placeholder={placeholder} 
            options={options} 
            variant='label-hidden'
            onchange={handleChange} 
            onblur={closePicklist} 
            class="slds-popover slds-popover_edit slds-popover__body"  
            dropdown-alignment="auto"></lightning-combobox>
        </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>

picklistColumn.js (Combobox LWC)

import { LightningElement, api, track } from 'lwc';
 
export default class PicklistColumn extends LightningElement {
    @api label;
    @api placeholder;
    @api options;
    @api value;
    @api context;
    showPicklist = false;
 
    connectedCallback(){
        console.log("Options is "+JSON.stringify(this.options));
        console.log("Value is "+JSON.stringify(this.value));
    }
 
    closePicklist() {
        this.showPicklist = false;
    }
 
    handleChange(event) {
        //show the selected value on UI
        this.value = event.detail.value;
        console.log('LINE27:Picklist Column event detail'+ JSON.stringify(event.detail));
        console.log('LINE27:Picklist Column context'+ this.context);
        //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.showPicklist = true;
    }
}
1
  • 1
    It's good to have the code here.... but it's also good to try to reduce it to only the relevant code causing the error (if you can) Sep 14 at 21:16

2 Answers 2

1

I think the tracking one the object that fires the @wire adapter might not be able to go as deep as you want it to.

How about making a getter like this:

get defaultRecordTypeId(){
  return this.onbAppDocObjectMetadata?.data?.defaultRecordTypeId;
}

Then make this your reactive @wire parameter:

@wire(getPicklistValues, { recordTypeId: '$defaultRecordTypeId',
                           fieldApiName: TYPE_FIELD })

Also, your picklistOptions array is not tracked. Try adding @track to this array.

4
  • Thanks for the suggestion Caspar. That didn't seem to resolve the issue though. I'm seeing the picklist options being stored in the console log on Line 49. However, they're not passing through as values or options in the custom picklist column table. Sep 14 at 23:43
  • It might be worth noting that in the Line 60 console log, the picklist options do not appear as being stored. Sep 14 at 23:51
  • You are not tracking this array, which ... may not matter, given that you are assigning a whole new array to it. But try anyway. Sep 15 at 0:41
  • Closer! After adding @track to picklistOptions, I see the options are present in the Line60 console log array. Still undefined in the extended custom picklist table however. Sep 15 at 2:36
2

Alright, I figured it out. Turned out to be a typo in this part of the code:

{
    label: 'Document Type',
    fieldName: 'Document_Type__c',
    type: 'PicklistColumn',
    editable: false,
    wrapText: true,
        typeAttributes: {
            options: { fieldName : 'picklistOptions' },
            value: { fieldName : 'Document_Type__c'},
            placeholder: 'Choose a Document Type',
            context: { fieldName : 'Id'}
        }

There was a typo on the options fieldName 'picklistOptions'. After updating to reflect 'pickListOptions', which is how it is referenced elsewhere, I can now see the full list of options within the table.

Table Options

Will mark Caspar's answer as correct as well since this wouldn't have worked without tracking picklistOptions.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.