+ Start a Discussion
EnreecoEnreeco 

External libraries and loading problems

Hi big audience!
I've developer several components since the release of Lightning framework but I still have a strange problem.
When I load external libraries (most common case is Bootstrap and jQuery) sometimes the jQuery library is not correctly loaded on startup, thus causing problems on dynamically loaded UI.
The docs say I should use the "renderer" with the afterRender event, but doing this I don't see any trace of jQuery (like it is not yet loaded, the $ plugin is undefined), so I can't init my UI from that method.
This leads to random crash of the app on first load, because sometimes the jQUery library is not loaded on startup.
Do you have any suggestions on this?
I'm currently using "setTimeout" to run the UI initializazione asynchronously, but I don't think this is a clean and good solution.
Thanks
Enrico
 
Best Answer chosen by Enreeco
SkipSaulsSkipSauls
Hi Peter,

Polling isn't really idea as the timing may vary depending on the device, network, etc., and between standalone apps and the Salesforce1 Mobile App. Using a loader such as RequireJS works well, and a few folks have written samples that use this. Here's an unmanaged package that includes events, components, static resources, and a sample app that uses RequireJS to load jQuery and Bootstrap:

https://login.salesforce.com/packaging/installPackage.apexp?p0=04tB000000011BS

Because it's unmanaged, you will need to change the namespace from aotp1 to your own. Once you've done that you can access the test app with a URL similar to this:

https://XXX.lightning.force.com/yyyy/requiresTestApp.app (https://gs0.lightning.force.com/ltng2/requiresTestApp.app)

You can also use Setup to add the requiresTestTab to S1. It's the same as the standalone app, just in a component that can be accessed from the "stage left" menu in S1. These components demonstrate using a namespaced version of Bootstrap, along with setting up Bootstrap and jQuery via different ways. They are integrated via attributes, events, etc. jQuery selectors are constrained to the component, something that's important to consider as components ≠ full-page apps. A jQuery plugin to do this automatically is a good candidate for another sample.

Screenshot of requiresTest running in S1

We're working on how to best support this in the product. In the meantime samples such as this can help many users. If you improve on this, or find that lighter weight loaders such as headLoad work well, I'd be interested in hearing more.

Thanks,

Skip
 

All Answers

Mark Leonard 8Mark Leonard 8
Have you tried using a <script> tag with a Salesforce static resource instead?
See https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/apps_csp.htm#apps_csp
Note there is an error in that doc sample and the syntax should be:
 <script src="/resource/resourceName" type="text/javascript"></script>
EnreecoEnreeco
Hi Mark,
this is the only supported way to load an external javascript into a lightning application, so this is exactly what I'm doing.
The problem is that randomly the jQUery library is not loaded when the "app starts" but like few milliseconds after the app is rendered (or so it seems), so the Bootstrap library fails...
Pete KnollePete Knolle
I can confirm that I see the exact same behavior.  The JavaScript files eventaully get loaded, because I can access them in the Chrome developer console.  The documentation for afterRender has "If you want to use a library, such as jQuery, to access the DOM, use it in afterRender().", but it does not seem to work.
Pete KnollePete Knolle
I posted on the Salesforce Stack Exchange my exact scenario.  See How to use jQuery (or any JS lib) in initial post rendering of Lightning Components (http://salesforce.stackexchange.com/q/54106/154)
Pete KnollePete Knolle
I believe I figured it out. Documented in my specific case here (http://salesforce.stackexchange.com/a/54111/154).  Here's my thoughts.

The scripts are loaded asynchronously which means that any code executed after they load that depdends on them being completely loaded may not work depending on whether or not they finish loading.  So, technically the code that runs the afterRender is being invoked after the code to load the JavaScript, it's just that the JavaScript loading doesn't finish fast enough.

A workaround is to set up a polling mechanism that checks for the existence of something in the library, e.g., check for jQuery / $.
 
({
    	
        afterRender: function(component, helper) {   
            var timesToCheck = 10;
            var poll = function () {
      			setTimeout(function () {
                    timesToCheck--;
                    if (typeof $ !== 'undefined') {
                        console.log($);
                    } else if (timesToCheck > 0) {
                        poll();
                    } else {
                        console.log('giving up')
        			}
      			}, 100);
    		};
    
    		poll();
        }
})


 
SkipSaulsSkipSauls
Hi Peter,

Polling isn't really idea as the timing may vary depending on the device, network, etc., and between standalone apps and the Salesforce1 Mobile App. Using a loader such as RequireJS works well, and a few folks have written samples that use this. Here's an unmanaged package that includes events, components, static resources, and a sample app that uses RequireJS to load jQuery and Bootstrap:

https://login.salesforce.com/packaging/installPackage.apexp?p0=04tB000000011BS

Because it's unmanaged, you will need to change the namespace from aotp1 to your own. Once you've done that you can access the test app with a URL similar to this:

https://XXX.lightning.force.com/yyyy/requiresTestApp.app (https://gs0.lightning.force.com/ltng2/requiresTestApp.app)

You can also use Setup to add the requiresTestTab to S1. It's the same as the standalone app, just in a component that can be accessed from the "stage left" menu in S1. These components demonstrate using a namespaced version of Bootstrap, along with setting up Bootstrap and jQuery via different ways. They are integrated via attributes, events, etc. jQuery selectors are constrained to the component, something that's important to consider as components ≠ full-page apps. A jQuery plugin to do this automatically is a good candidate for another sample.

Screenshot of requiresTest running in S1

We're working on how to best support this in the product. In the meantime samples such as this can help many users. If you improve on this, or find that lighter weight loaders such as headLoad work well, I'd be interested in hearing more.

Thanks,

Skip
 
This was selected as the best answer
EnreecoEnreeco
This is exatcly what I was looking for. I'll use this solution in my blog post http://enreeco.blogspot.it/2014/10/salesforce-lightning-loading-scripts.html 
SkipSaulsSkipSauls
Updates as of 10/30/2014:

I've updated the sample to include the script loading in the requires component, as Peter suggested. Included are:

The requires component is now defined as follows:
<aura:component>
    <aura:attribute name="baseUrl" type="String" default="/resource/"/>
    <aura:attribute name="styles" type="Map"/>
    <aura:attribute name="scripts" type="Map"/>
    <aura:attribute name="deps" type="Map"/>
    <aura:attribute name="initScripts" type="Aura.Action"/>
    <aura:registerEvent name="requiresReady" type="aotp1:requiresReady"/>
    <aura:handler name="init" value="{!this}" action="{!c.init}"/>
</aura:component>

The requiresTest2 component uses the requires component as follows (note the ready flag used to indicate that the component has been initialized):
<aura:attribute name="ready" type="Boolean" default="false" description="Used to check if resources have been loaded"/>
    <aotp1:requires 
  		baseUrl="/resource/"
     	scripts="{jquery:'jquery/jquery.js',bootstrap:'aotp_bootstrap/js/bootstrap.js'}"
      	deps="{bootstrap:['jquery']}"
     	styles="{boostrap:'aotp_bootstrap/css/aotp_bootstrap.css'}"
		requiresReady="c.init"
	/>

The requiresTest2 helper now has an initHandlers function that is called from the requiresReady event and the renderer. The ready flag mentioned above is used to check whether to proceed with the init. This is necessary because the loading order can vary depending on the container, for example the Lightning app vs. the Salesforce1 Mobile App tab.

The requiresReady event is now COMPONENT scoped, and includes the objects returned by the requirejs callback, using the names specified in the attributes.

Version 1.2 installation URL:

https://login.salesforce.com/packaging/installPackage.apexp?p0=04tB000000011BX

As with any samples, no guarantees are made, but I am interested in feedback or improvements!
tural sadiktural sadik
Currently I am having trouble using this code. When debugging I found out that values marked as type:Map do not have toJSON method so kn owing that javascript itself stores every object as a map/dictionary what I did is I commented out toJSON calls. I am not sure if I am missing something. Can you please let me know what is the right way?
rahul mamtani 6rahul mamtani 6
@SkipSauls
I am trying to install the package from given url but installation is failed.I dont know what is the problem.Can you please help me?
David Helgerson 3David Helgerson 3
@SkipSauls

I'm also getting an installation error,  759792478-169904 (996808149). I have lightning components enabled. Is there an additional permission or setting needed?
Moshe Karmel 9Moshe Karmel 9
There is now a built in component, called "ltng:require", available to load external libraries. You can check it out here: http://docs.releasenotes.salesforce.com/en-us/spring15/release-notes/rn_lightning_load_resources.htm
Ega RakeshEga Rakesh
Hi Skip ,

i am unabel to install the package and getting below error. i think the error i sdue to namespace

No COMPONENT named markup://aotp1:requiresTest2 found : [markup://c:requiresTestApp] : No COMPONENT named markup://aotp1:requiresTest2 found : [markup://c:requiresTestApp]

could you please provide another way to get the code, like github link.

Thanks,
Rakesh
Omar Salas 8Omar Salas 8
Hola! has anyone been able to resolve the following error?


No COMPONENT named markup://aotp1:requiresTest2 found : [markup://c:requiresTestApp] : No COMPONENT named markup://aotp1:requiresTest2 found : [markup://c:requiresTestApp]

Thanks!

Omar
Alba AzconaAlba Azcona
Omar, that error was happening to me and in the end it was I had wrongly created a VF component instead of a Lightning Component, so my org was not able of finding it, silly!! :( Be careful with the namespace as well
Alba AzconaAlba Azcona
I mean that error happened to me trying to use my own component somewhere else :)
Jasveer SinghJasveer Singh
Include Jquery into lightning Component

Go to Setup select Critical Update

Deactivate:  Enable Lightning LockerService Security

and paste below code in developer console


1 Step: Insert into component

<ltng:require scripts="/resource/Jq"  afterScriptsLoaded="{!c.afterScriptsLoaded}" />
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 Step: Insert into Component

<button id="button1" onclick="{!c.dropdown}" class="slds-button slds-button--neutral">Show/Hide Columns
                <img src="/resource/dropdownIcon" style="height:20px; width:20px;"/></button>
<p id="dropdown1">hi</p>
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Step 3: insert into Controller

({
    afterScriptsLoaded : function(component, event, helper) {
        
        },
    
    dropdown:function(component, event, helper)
    {
      //  console.log("1st time");
       //  $.noConflict();
        jQuery("#dropdown1").toggle();
//console.log("2nd time");
    }
    
})