[SalesForce] LWC show loading spinner dynamically

I have a Parent and Child component I am working with.

The parent has a button that launches a modal, and in that modal body slot, it loads my child component.

In the parent, I have a spinner that is shown by default, and after It has loaded the necessary data within the @wire, it sets the loader to false.

// Parent Component JS
export default class CreateCaseApp extends LightningElement {
  @api recordId;
  @api contactRecord;
  @track isLoadingIndicator = true;

  @wire(getRecord, {
    recordId: "$recordId",
    fields: [FIELD_CONTACT_NAME]
  })
  contactData(result) {
    let { data, error } = result;
    if (data) {
      this.contactRecord = data;
      this.toggleLoading(false);
    }
    if (error) {
      errorHandler("Unable to get Contact Record", error);
    }
  }

  /**
   * Event Listener: Child dispatched loading event
   */
  handleIsLoadingChangeEvent(event) {
    this.isLoadingIndicator = event.detail;
  }

  /**
   * Getter: Return loading indicator
   */
  get isLoading() {
    return this.isLoadingIndicator;
  }
}

// Parent Component HTML
<div slot="body">

    <!-- Loader -->
    <template if:true={isLoading}>
        <div class="slds-is-relative" style="min-height: 10vh;">
            <lightning-spinner alternative-text="Loading..." size="medium">
            </lightning-spinner>
        </div>
    </template>

    <!-- Component -->
    <c-create-enterprise-case
        if:false={isLoading}
        contact={contact}
        onisloadingevent={handleIsLoadingChangeEvent}
    >
    </c-create-enterprise-case>

</div>

Now, the child component that is loaded into the modal also does a callout for some data. The connectedCallback callback dispatches an event to tell the parent to show the loading indicator while the child component makes its callout.

After the callback has data, it dispatches another event to tell it to hide the loading indicator. Pretty straight forward with what I want to do.

// Child Component JS
export default class CreateEnterpriseCase extends LightningElement {

    connectedCallback() {
        this.setup();
    }

    setup(){
        this.toggleLoading(true);
        this.doCallout();
    }

    doCallout(){
        // APEX Callout for data
        ...
        if(data){
            this.toggleLoading(false);
        }
    }

    toggleLoading(status) {
        this.dispatchEvent(
            new CustomEvent("isloadingevent", {
                detail: status
            })
        );
    }
}

Here is my issue..

My child component is loaded conditionally with if:true{isLoading}. When the child component loads and makes its callout, it tells the parent to set isLoading=true which removes the child component from the DOM. When the data in the child component has loaded, it dispatches another event setting isLoading=false which then conditionally loads the child component again.

Since this child component makes a callout on initialization, it's getting stuck in a loop where it keeps adding/removing from the DOM and each time, makes those callouts.

How can I go about showing this status indicator in my modal conditionally, without destroying my child component due to the conditional loading of it?

I tried to just remove the conditional check on the child, but then the spinner is just above the child component and the child still renders its data and it looks odd.

My end goal is to have the whole modal body show an overlay/spinner conditionally, where either the other modal contents are not shown, or the spinner is on top of the other data so you can't interact with it until it is done loading.

enter image description here

Best Answer

Personally, I would decouple the spinners and have each component show its own. That suggests that you can keep the conditional rendering of the child so it only appears when the parent is not still loading. However, the child can show its own spinner, taking up its visual space in the container, until it has separately loaded its data at which point it then renders its section (hiding the spinner).

Trying to combine them to use a single spinner means you cannot consider the components in isolation. This might be fine, but in that case you might as well have a single component.

Related Topic