[SalesForce] Lightning Components: How to make sure that a script loaded via ltng:require is loaded in aferRender()

I'm trying to load jquery and use it for dom manipulation. Therefore I need to find the right place to invoke the $() call.

In the component, I load jquery like this

<ltng:require scripts="{!$Resource.elfJquery_js}" />

In the renderer (knowing I have a div), my code is

afterRender: function (cmp, helper) {
    var afterRend = this.superAfterRender();
    $("div").addAttr('test'); 
    return afterRend;
},

It seems to happen, that jquery is not loaded at the time afterRender() is executed. I got errors, that $ is not defined. I assume due to async loading done by ltng:require. Is there any easy way to make sure libs are loaded at the time of first afterRender() or any other place to put scripts which needs to process the finaly generated markup?

Best Answer

As per @TrevorBliss comments , adding afterScriptsLoaded to the ltng:require is sufficient which means there's no need for the renderer.

If you really need to do this in the renderer, then you need to do below:

  1. Use rerender instead of afterRender.
  2. Have a flag to indicate the script has been loaded and set it's value to true in afterScriptsLoaded method of ltng:require.

What would have happen here is:

  1. Component loads and it's init handler will fire(if you have one).

  2. Frameworks finishes the rendering the DOM in the browser.

  3. ltng:require's afterScriptsLoaded method will be called which in turn sets an aura:attribute value to true, thus causing the framework to run the rerender method of the component.

So our best choice is, to use rerender because it is called after the afterRender which in turn guarantees availability of DOM and also we are 100% sure that scripts are loaded(since it is invoked due the flag setting).

Be cautious when using rerender method, becauses it is called many times during the component's lifecycle. Generally it will be called, whenever you change the value of an aura:attribute.

Here's a sample component to demonstrate the flow:

Component:

<aura:component>
    <ltng:require scripts="{!$Resource.Jquery_js}" afterScriptsLoaded="{!c.setScriptLoaded}"/>
    <aura:attribute name="isJqueryLoaded" type="Boolean" description="Flag to indicate jquery is loaded"/>
    <aura:attribute name="needToProcessReRenderLogic" type="Boolean" description="Flag to indicate need to process the logic rerender since it is called many times during the component's lifecycle"/>
<aura:component>

Controller:

({
    "setScriptLoaded" : function(cmp){
        cmp.set("v.isJqueryLoaded",true);
        cmp.set("v.needToProcessReRenderLogic",true);
    }
})

Renderer:

({
    rerender : function(cmp, helper){
        this.superRerender();

        if(cmp.get("v.isJqueryLoaded") && cmp.get("v.needToProcessReRenderLogic")) {
            //your logics

            //Finally set the needToProcessReRenderLogic to false, since rerender will be called multiple times
            cmp.set("v.needToProcessReRenderLogic",false); // this will not fire rerender again
        }
    }
})