[SalesForce] Chain Apex Methods in LWC

I am having a situation where I'm trying to pop an alert on an opportunity, but it's conditional based on data on the current user, and data on a custom object.

I noticed that on refreshes the alert doesn't pop consistently.

Original Approach: Produced Inconsistent Results

The idea is that the user has data used for a second server call. So I had chained together 3 components.

Parent Component - Get User Data @wire decorator to fire aura enabled apex method 
 |_ Child Component 1 - Check SF for specific record that has user data on it. @wire decorator to fire aura enabled apex method  
      |___Child Component 2 - Fire Alert if Child Component returns data 

However, Parent component fires every time, but the second Child does not fire consistently.

So my thought was to try promises.

New Approach: Trying Promises

I decided I would use the connectedCallBack() function to attempt to control the server side calls.

Essentially, server side call to get user; if user is found, fire second server side call, and then begin logic on child components. Problem: I can't get the second promise to fire in this setup. I'm sure it's a syntax issue.

HTML – Parent

<template>

    <template if:true={userObj.ctmUserId}>
        <c-opp-contact-phone-alert
            ctm-user-id={userObj.ctmUserId}
            record-id={recordId}
        >
        </c-opp-contact-phone-alert>
    </template>

</template>

JS – Parent

connectedCallback(){
        console.log('CONNECTED CALLBACK');
        getCurrentUser({userId: USER_ID}).then(result => {
            console.log('IN PROMISE: USER');
            this.userObj.ctmUserId = result.CTM_Agent_Id__c;
            return lookupExistingPhone({curUserCtmId: this.userObj.ctmUserId})

        }).then((result) => {
            console.log('IN LOOKUP PROMISE');
            this.ctmRecord = result;
            console.log(this.ctmRecord);
        })
    }

Best Answer

There's a much easier way to do this in LWC:

async connectedCallback() {
  this.userObj.ctmUserId = await getCurrentUser({userId: USER_ID}).CTM_Agent_Id__c;
  this.ctmRecord = await lookupExistingPhone({curUserCtmId: this.userObj.ctmUserId});
}

This allows you to wait asynchronously automatically without all the complicated setup.

You can read more about async/await on MDN.

I wrote a demo component that calls two methods in a row, both by async/await and promise chains.

public with sharing class q310887 {
  @AuraEnabled
  public static String method1() {
    return 'Hello';
  }
  @AuraEnabled
  public static String method2(String param) {
    return param + ' World';
  }
}

<template>
  <div>
    {output}
  </div>
  <lightning-button onclick={asyncCall} label="Async/Await">
  </lightning-button>
  <lightning-button onclick={promiseCall} label="Promise Chain">
  </lightning-button>
</template>

import { LightningElement } from "lwc";
import method1 from "@salesforce/apex/Q310887.method1";
import method2 from "@salesforce/apex/Q310887.method2";

export default class Q310887 extends LightningElement {
  output = "Waiting...";
  connectedCallback() {}
  async asyncCall(event) {
    this.output = "Waiting...";
    this.output = await method1();
    this.output = await method2({ param: this.output });
  }
  promiseCall(event) {
    this.output = "Waiting...";
    method1()
      .then((result) => ((this.output = result), method2({ param: result })))
      .then((result) => (this.output = result));
  }
}

I also tested this in IE 11, and both methods also work. There should be no problem using either technique in the code. One small explanation is in order here.

When you use (...) => expr, the result of the expression is returned back. I use this to return the value of the next promise to continue the chain. The , operator performs each action (typically assignment), and discards all but the last result. I use this to assign the value from the first method before passing it to the next. Note that the "extra" parentheses are actually necessary because of the operator precedence of ,; without it, JavaScript presumes that the comma is introducing a new parameter to the method instead of being the , operator.

Related Topic