0

I'm using an apex method to update related records in a custom Related List LWC using the onsave method on Lightning-Datatable. The Apex Method is updating the records correctly, however the "Save" and "Cancel" buttons remain on the page and the LWC records are not refreshed as intended.

I do get a warning in the Javascript console which states:

WARNING: Unsupported [object Object],[object Object] method: toJSON. Returning undefined

However this does not prevent the Save method from completing correctly on the Server/Apex side.

Can someone let me know what I'm doing incorrectly perhaps? Thanks!

<template>
    <div class="slds-page-header slds-page-header_related-list">
        <lightning-card title="Invoice Billing Contacts" icon-name="standard:team_member">
            <div slot="actions">
                <!--lightning-button label="Save" title="Save" icon-name="utility:save" class="slds-m-left_x-small" onclick={handleSave}></lightning-button-->
                <lightning-button label="Import" title="Import" icon-name="utility:adduser" class="slds-m-left_x-small" onclick={importContact}></lightning-button>
            </div>
        </lightning-card>
    </div>
    <div>
        <lightning-datatable key-field="Id" data={invoiceBillingContacts} show-row-number-column
            row-number-offset={rowOffset} columns={columns} onsave={handleSave}>
        </lightning-datatable>
    </div>
</template>

import { LightningElement, api, wire, track } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
import getInvoiceBillingContacts from '@salesforce/apex/InvoiceBillingContactTriggerHelper.getInvoiceBillingContacts';
import importInvoiceBillingContacts from '@salesforce/apex/InvoiceBillingContactTriggerHelper.importInvoiceBillingContacts';
import updateInvoiceBillingContact from '@salesforce/apex/InvoiceBillingContactTriggerHelper.updateInvoiceBillingContact';
import { refreshApex } from '@salesforce/apex';

const columns = [
    { label: 'IBC Number', fieldName: 'Name', editable: false},
    { label: 'Contact', fieldName: 'Contact_Name__c', type: 'text', editable: false },
    { label: 'Contract', fieldName: 'Contract_Name__c', type: 'text', editable: false },
    { label: 'Invoice Email', fieldName: 'Invoice_Email__c', type: 'email', editable: false },
    { label: 'Enabled for this Contract', fieldName: 'Enabled_for_this_Contract__c', type: 'boolean', editable: true },
];

export default class InvoiceBillingContactList extends LightningElement {

    @api recordId;
    invoiceBillingContacts = [];
    error;

    columns = columns;
    rowOffset = 0;

    _wiredResult;
    saveDraftValues;

    @wire(getInvoiceBillingContacts, {contractId: '$recordId'})
    wiredIBC(result) {
        this._wiredResult = result;
        let { data, error } = result;
        if (data) {
            this.invoiceBillingContacts = data;
            this.error = undefined;
        } else if (error) {
            this.error = error;
            this.invoiceBillingContacts = undefined;
        }
    }

    importContact(){

        importInvoiceBillingContacts({recordId: this.recordId})
        .then(() => {
            console.log('refreshing Contacts $$');
            return refreshApex(this._wiredResult);
        })
        .catch((error) => {
            this.message = 'Error received: code' + error.errorCode + ', ' +
                'message ' + error.body.message;
            console.log(this.message);
        });

    }

    handleSave(event) {
        this.saveDraftValues = event.detail.draftValues;
        console.log('%%%' + this.saveDraftValues);

        updateInvoiceBillingContact({ invoiceBillContacts : this.saveDraftValues})
        .then(() => {
            console.log('refreshing Contacts after save');
            return refreshApex(this._wiredResult);
        })
        .catch((error) => {
            this.message = 'Error received: code' + error.errorCode + ', ' +
            'message ' + error.body.message;
            console.log(this.message);
        });
    }


}
  • I had the same issue and this is how I've fixed it, 1. Move the getInvoiceBillingContacts function to a separate function 2. Call it from connectedCallback function 3. After save, call the function created in step 1 I would like to see any better solution – SL man Sep 22 at 20:33
  • Thanks for the answer. What throws me off is that the refreshApex method works correctly on the importContact() method - not sure why there's a change in behavior. – cg13 Sep 23 at 20:18
1
+50

Below quotes are from the Data table inline editing documentation, where you can see a similar scenario to you being called out (Apex updates from onsave datatable):

First, the "Save" and "Cancel" buttons will only go away when you clear the draftValues property which it doesn't appear you've done.

After making changes, when a user presses the Tab key or clicks outside the cell, the datatable footer appears with the Cancel and Save buttons. To hide the datatable footer, clear the draftValues property.

In terms of what's different with importContact and handleSave, the handleSave is the event from the data table updating potentially multiple rows with an apex method. The below is mentioned for this situation

Since the records are updated by Apex, you must notify Lightning Data Service using the getRecordNotifyChange(recordIds) function so that the Lightning Data Service cache and wires are refreshed.

It then calls out to ensure the timing of getRecordNotifyChange() is after the record update via Apex - use the async/await pattern.

With all that said, you should be able to incorporate those notes like so:

import { getRecord, getRecordNotifyChange } from 'lightning/uiRecordApi';

....

async handleSave(event) {
        this.saveDraftValues = event.detail.draftValues;
        // Prepare the record IDs for getRecordNotifyChange()
        const notifyChangeIds = updatedFields.map(row => { return { "recordId": row.Id } });

        await updateInvoiceBillingContact({ invoiceBillContacts : this.saveDraftValues})
        .then(() => {
            console.log('refreshing Contacts after save');

            //Refresh LDS cache and wires
            getRecordNotifyChange(notifyChangeIds);

            return refreshApex(this.wiredIBC).then(() => {
                 //clear all draft values
                 this.draftValues = [];
            });

        })
        .catch((error) => {
            this.message = 'Error received: code' + error.errorCode + ', ' +
            'message ' + error.body.message;
            console.log(this.message);
        });
    }

| improve this answer | |
  • Thank you Kris. This was the perfect documentation I needed - I poured over the Datatable component in the Library documentation and completely missed this perfect example in the Dev Guide - I feel a bit silly. I was able to resolve my issue by utilizing the documentation and help provided by yourself. Thanks! – cg13 Sep 25 at 14:02

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.