13

Ok,

So I am trying to implement a Spinner whenever my component is doing something server side. It is working just fine EXCEPT:

When the Lightning component is in the Lightning Page it seems the waiting and done waiting event is called like 20 times

I have this in my component:

<aura:handler event="aura:waiting" action="{!c.toggleSpinner}"/>
<aura:handler event="aura:doneWaiting" action="{!c.toggleSpinner}"/>

And the spinner appears and disappears like 10 times load which is NOT ideal.

What I have had to do is move the toggle spinner code to a helper method for every component (parent and children) so the code exists is 3 difference place

And then in every single method that makes a server call I have had to add

helper.toggleSpinner(component);

Before the enque

and

helper.toggleSpinner(component);

within the callback.

This seems like a waste of code to me and was curious if there is a better way to handle a spinner during server calls across a Lightning app that does not duplicate code everywhere

If you need specific example to replicate let me know but I believe the above is pretty clear

Note: I have seen this : Lightning Components executing logic on init state only and attempted to implement and it did reduce the calls to the spinner toggle from like 20-30 down to < 10. But more than once is too much during init.

Here is an example. Simply place in the contact standard page layout as look at the console and see how many times it gets called (over 10)

Component

<aura:component description="myProblemComponent" implements="flexipage:availableForAllPageTypes,force:hasRecordId">

    <aura:handler event="aura:waiting" action="{!c.showWaiting}"/>
    <aura:handler event="aura:doneWaiting" action="{!c.hideWaiting}"/>
</aura:component>

Controller

({
    showWaiting: function(component){
        console.log('Show Waiting');
    },
    hideWaiting: function(component){
        console.log('Hide Waiting');
    }
})

Partial output

enter image description here

4
  • aura:waiting may be called multiple times, but I would imagine that so long as you queued all your loads at once (e.g. multiple actions during init), then aura:doneWaiting would work as you'd imagine it should. That said, I think instead of "toggleWaiting" you should simply have waiting->showWaiting and doneWaiting->hideWaiting. That way you don't need to remember to toggle it before each enqueue call.
    – sfdcfox
    Commented Jan 9, 2017 at 22:34
  • @sfdcfox - unfortunately not the case. See updated question with example. This was placed in the contact Lightning Page on the right side. Problem is The spinner I am creating conflicts with SF default spinner and you can see it hiding and showing multiple times. I simply want to set it up and use it with a single code block. Guess it is not possible and I have to create helper methods for every controller
    – Eric
    Commented Jan 9, 2017 at 22:45
  • 1
    @Eric: There are 2 ways to do the spinner. You can create a component for the spinner include it in the components you want to use and give it a aura:id. Using component.find you can call the methods in it.2.The other way is to create a base component with the methods for spinner(like show spinner,hide spinner) and extend your other component then automatically the helper method from the base components will get inherited. something like below <aura:component controller="ABCCtrl" extends="c:BaseComp">. The base comp helper will container the spinner code which can be called from ABCCtrl.
    – RedDevil
    Commented Jan 10, 2017 at 2:10
  • 2
    @RedDevil - Thanks for the answer but I think you may have missed the point of the question.
    – Eric
    Commented Jan 10, 2017 at 2:16

3 Answers 3

8

The aura:waiting and aura:doneWaiting events are fired at the start/end of every Aura action, even ones you don't fire yourself. Unless your component is running in complete isolation (standalone Aura app outside of the Lightning platform), this is probably not what you want. Even if you manage to ignore the events during your components init, the container app may fire server-side actions in the background and trigger your event handlers.

Manually toggling the spinner is probably your best bet. There are multiple ways to reduce code duplication rather than copy/pasting the same helper method everywhere. One way is to create your own custom event to toggle the spinner. Then you just have to make sure you fire the toggle event at the correct places and the code to show/hide the spinner can be consolidated to one spot. Another option is to use component inheritance like @RedDevil suggested in the comments.

3
  • Yea, I guess events is the way to go, Application level I am assuming. But then I need event registration in every component, code to fire it, etc. For others who come along would you care to show an example of events, register event, and fire event? If not I may do it tomorrow and add it to my question
    – Eric
    Commented Jan 10, 2017 at 5:51
  • @Trevor: Would you mind adding code to your answer. Everything I find on Google related to this topics seems to show the WRONG implementation using the global aura events. Commented May 18, 2017 at 8:49
  • 1
    @RobertSösemann - see my answer below
    – Eric
    Commented Jun 6, 2017 at 21:09
4

So here is how I currently see it and I may be wrong so please feel free to add an answer with a different take:

If your Spinner is in a main component and you want to show / hide it from any component in the application you would do the following:

Application Event

<aura:event type="APPLICATION" description="showSpinner">
</aura:event>

<aura:event type="APPLICATION" description="hideSpinner">
</aura:event>

Parent Component

....
<aura:handler event="c:showSpinner" action="{!c.showSpinner}"/>
<aura:handler event="c:hideSpinner" action="{!c.hideSpinner}"/>

<lightning:spinner aura:id="spinner" variant="brand" size="large"/>
.....

Component Controllers

Show

$A.get("e.c:showSpinner").fire();

Hide

$A.get("e.c:hideSpinner").fire();

--

If your Spinner is in a component and you want to show / hide it locally (can be combined with above to have multiple spinners):

Component

<lightning:spinner aura:id="spinner" class="slds-hide" variant="brand" size="large"/>

Controller / Helper

Show

$A.util.removeClass(
      component.find('spinner'), 
      "slds-hide"
);

Hide

$A.util.addClass(
      component.find('spinner'), 
      "slds-hide"
);

So which way you choose it depends on how, where, and when you want to show / hide the spinner.

Not sure I see the point of component inheritance (from the comments and answer above) or what value it would bring but would love to see an example

1

I did this in my code and a simpler way to invoke spinner instead of using Aura:waiting and Aura:doneWaiting tags.

When you have multiple components on a lightning page and if you use Aura:doneWaiting , i found that it will invoke multiple server side calls. the below note was mentioned here

We don't recommend using the legacy aura:doneWaiting event except as a last resort. The aura:doneWaiting application event is fired for every server response, even for responses from other components in your app. Unless your component is running in complete isolation in a standalone app and not included in Lightning Experience or Salesforce1, you probably don’t want to handle this application event. The container app may fire server-side actions and trigger your event handler multiple times.

I tried this and it worked for me.

Component:

<aura:attribute name="showSpinner" type="boolean" default="false"/>
<aura:if isTrue="{!v.showSpinner}">
    <div class="slds-spinner_container">
        <lightning:spinner aura:id="spinner" variant="brand" size="medium"/>
    </div>
</aura:if>

JS Controller:

({

    doInit : function(component, event, helper) {

// show spinner to true on click of a button / onload
        component.set("v.showSpinner", true);

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

        action.setParams({
            "CaseId" : component.get("v.recordId")
        });
        console.log("caseId :" + component.get("v.recordId"));

        action.setCallback(this, function(response) {

            var output = response.getReturnValue();
// on call back make it false ,spinner stops after data is retrieved
            component.set("v.showSpinner", false);

// do ur logic here

        });
        $A.enqueueAction(action);
    }
})

CSS :

.THIS .slds-spinner_container {  
    z-index: 10000;
    position: fixed;   
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .