[SalesForce] Reload Lightning Data Service after updating the record on the server

I have a parent component that uses lightning data service (lds) to get the record. This record is then shared to all of it's children as needed using data binding. I need to update the record using apex because some fields users are only allowed to update when using an api. After I make the callout, update the record, and then reload lds, the field values I retrieved from the api do not display in my UI. I need to manually refresh the page to see it, which is a bad user experience. Is it possible to reload lds after using apex to update the record? Here is what I did in my code.

Parent.cmp

<aura:attribute name="prescription" type="Case" />
<aura:attribute name="patient" type="Contact" />
<aura:attribute name="user" type="User" />

<aura:method name="reload" action="{!c.reload}" />

<force:recordData aura:id="prescriptionEditor" 
                  recordId="{!v.recordId}" 
                  targetFields="{!v.prescription}"  
                  fields="Id,CoPayResult__c,AdditionalInsuranceMessage__c,Deductible_Remaining__c,Patient_Pay_Amount__c,Plan_Paid_Amount__c,RejectionCode__c,Verification_Errors__c"
                  mode="EDIT" 
                  recordUpdated="{!c.handlePrescription}" />

<force:recordData aura:id="patientEditor" 
                  recordId="{!v.prescription.Patient__c}" 
                  targetFields="{!v.patient}"
                  fields="Id,Email" 
                  mode="EDIT" recordUpdated="{!c.handlePatient}" />

<force:recordData aura:id="userEditor" 
                  recordId="{!v.userId}" 
                  targetFields="{!v.user}" 
                  layoutType="FULL" 
                  mode="EDIT"
                  recordUpdated="{!c.handleUser}" />



        <c:Child prescription="{!v.prescription}"  
                 parent="{! this }" />

ParentController.js

({ 
    handlePrescription: function(component, event, helper) {
        let prescription = component.get("v.prescription");
        let eventParams = event.getParams();
        //console.log(eventParams.changeType);
        if(eventParams.changeType === "ERROR") {
            helper.reload(component);
        } else if(eventParams.changeType === "LOADED") {            
            //console.log("Prescription has loaded successfully.");
            //console.log(JSON.parse(JSON.stringify(prescription)));

            component.set("v.userId", $A.get("$SObjectType.CurrentUser.Id"));

            let patient = component.get("v.patient");
            let user = component.get("v.user");

            if(patient == null && user == null) {
                setTimeout($A.getCallback(function () { component.find("patientEditor").reloadRecord() })); 
                setTimeout($A.getCallback(function () { component.find("userEditor").reloadRecord() }));                 
            }                  
        } else if(eventParams.changeType === "CHANGED") {
            helper.reload(component);
        } else if(eventParams.changeType === "REMOVED") {
            // record is deleted
            //console.log("Record was removed.");
        }
    },
    handlePatient: function (component, event, helper) {
        let prescription = component.get("v.prescription");
        let patient = component.get("v.patient");
        var eventParams = event.getParams();

        if (eventParams.changeType === "LOADED") {
            // record is loaded (render other component which needs record data value)
            //console.log("Patient has loaded successfully.");
            //console.log(JSON.parse(JSON.stringify(component.get("v.patient"))));
        }
        else if (eventParams.changeType === "CHANGED") {
            // record is changed
            //console.log("Record was changed.");
        } else if (eventParams.changeType === "REMOVED") {
            // record is deleted
            //console.log("Record was removed.");
        } else if (eventParams.changeType === "ERROR") {
            // there’s an error while loading, saving, or deleting the record
            //console.log("error.");
        }
    }, 
    handleUser: function (component, event, helper) {
        let user = component.get("v.user");
        let eventParams = event.getParams();

        if (eventParams.changeType === "LOADED") {
            //console.log("User has loaded successfully.");
            //console.log(JSON.parse(JSON.stringify(user)));
        }
        else if (eventParams.changeType === "CHANGED") {
            // record is changed
            //console.log("Record was changed.");

        } else if (eventParams.changeType === "REMOVED") {
            // record is deleted
            //console.log("Record was removed.");
        } else if (eventParams.changeType === "ERROR") {
            // there’s an error while loading, saving, or deleting the record
        }
    },    
    reload: function (component, event, helper) {
        helper.reload(component);
    }    
})

ParentHelper.js

reload : function(component) {
    setTimeout($A.getCallback(function () { 
        console.log('reloaded');
        component.find("prescriptionEditor").reloadRecord();
        component.find("patientEditor").reloadRecord();                                   
    })); 
}

Child.cmp

<aura:component>
    <aura:attribute name="parent" type="Aura.Component"/>    
    <aura:attribute name="prescription" type="Case" />

    <div class="slds-grid slds-wrap">
        <aura:if isTrue="{!!v.editing}">
            <!-- Save/Cancel buttons -->
            <div class="slds-grid slds-medium-size--3-of-3 slds-grid_align-end">
                <div class="slds-p-left_xx-small">                  
                    <c:GrandChild prescription="{#v.prescription}"
                                                   parent="{!v.parent}"/>
                </div>
                <div class="slds-p-left_xx-small">
                    <lightning:button variant="brand" 
                                      label="Edit" 
                                      onclick="{!c.toggleEditing}"/>
                </div>
            </div>
            <!-- Edit button -->
            <aura:set attribute="else">
                <div style="text-align:right;" class="slds-medium-size--3-of-3">
                    <lightning:button variant="brand" label="Save" onclick="{!c.save}" />                
                    <lightning:button variant="brand" label="Cancel" onclick="{!c.toggleEditing}" />
                </div>
            </aura:set>
        </aura:if>      
    </div>
</aura:component>

GrandChild.cmp

<aura:component implements="force:lightningQuickAction" controller="GrandChildController" >
    <aura:attribute name="parent" type="Aura.Component"/>    
    <aura:attribute name="prescription" type="Object"/>

    <button class="slds-button slds-button--brand" onclick="{!c.callHealthConnexxAndClose}">Verify</button>

</aura:component>

GrandChildController.js

({

callHealthConnexxAndClose : function(component, event, helper) 
{

    var action = component.get("c.getInsuranceVerification");

    action.setParams({caseId: component.get("v.prescription.Id") });

    action.setCallback(this, function(response) 
    {

        var state = response.getState();
        if (state === "SUCCESS") {

        } else if (state === "INCOMPLETE") {    

        } else if (state === "ERROR") {
            console.log("Failed with state: " + state);
            console.log(response.error[0].message);
        }       


        let p = component.get("v.parent");
        p.reload();   
    });

    $A.enqueueAction(action);
},

})

GrandChildController.apxc

public class GrandChildController {
    @AuraEnabled
    public static void getInsuranceVerification(Id caseId) {
        //System.debug('Received ' + caseIds.size() + ' Cases');

        List<Case> casesToUpdate = new List<Case>();
        Case c = [SELECT Id FROM Case WHERE Id = :caseIds]


            VerificationRequest req = new VerificationRequest(c);

            VerificationResponse response = sendVerRequest(req);

            if(response.calloutSuccess){
                if(response.Reply.Message != null) {
                    String message = 'Response from TripleFIN: ' + response.Reply.Message;
                    AuraHandledException auraError = new AuraHandledException(message);
                    auraError.setMessage(message);
                    throw auraError;                    
                }

                Case p = new Case();

                p.Id = c.Id;
                p.CoPayResult__c = response.Reply.CoPay;
                p.AdditionalInsuranceMessage__c = response.Reply.AdditionalInfo;
                p.Deductible_Remaining__c   = response.Reply.RemainingDeductableAmount;
                p.Patient_Pay_Amount__c = response.Reply.PatientPay;
                p.Plan_Paid_Amount__c   = response.Reply.TotalAmountPaid;
                String rejectCodeStr = '';
                for(RejectCodes rc : response.Reply.RejectCodes){
                    rejectCodeStr += String.ValueOf(rc.RejectCode) + '; ';
                }
                p.RejectionCode__c = rejectCodeStr;
                p.Verification_Errors__c = response.Reply.Errors != null ? String.valueOf(response.Reply.Errors) : null;

                try {
                    update p;
                } catch (DMLException e) {
                    String message = e.getMessage();
                    AuraHandledException auraError = new AuraHandledException(message);
                    auraError.setMessage(message);
                    throw auraError;                        
                }
            }

}
}

I know this code is working, because when I refresh my UI, the values are there that are pulled in GrandChildController.apxc. The problem appears to be when I'm calling reload() from within GrandChild.cmp

Best Answer

What I feel here is in force:recordData the reloadRecord method when called is not doing a server round-trip to fetch new values. It's displaying the stale value from local cache.

From SF docs of reloadRecord method

Performs the same load function as on init, using current config values (recordId, layoutType, mode, etc) but does not force a server trip if not required

Somehow SF feels that its best to fetch values from local cache, You have to override this behavior by passing a boolean parameter(skipCache) to reloadRecord method which will force a server roundtrip to get the latest value from the database.

Thus your Parent Helper code will be

reload : function(component) {
    setTimeout($A.getCallback(function () { 
        console.log('reloaded');
        component.find("prescriptionEditor").reloadRecord(true);
        component.find("patientEditor").reloadRecord(true);                                   
    })); 
}

Src: https://developer.salesforce.com/docs/component-library/bundle/force:recordData/specification

Related Topic