[SalesForce] uncaught ReferenceError: <> is not defined when using external js in lightning

I'm actually referencing to a js method defined in my external js file, in my lightning component. But it refuses to recognise my method. Here is my code :

visualforce page :

<apex:page showheader="false" standardstylesheets="false" controller="VFC01_DisplaySimpleLayoutDetails">

<apex:includelightning />
<div id="xi-div-container" />

<script>
    var wsname = "{!$CurrentPage.parameters.wsname}"; // the webservice to be called
    var template = JSON.parse('{!strResult}');
    var abbreviations = JSON.parse('{!strAbbreviations}');        
    var param1 = "{!$CurrentPage.parameters.param1}"; // the parameter to pass to the ws

    $Lightning.use("c:LA01_GeneratePlainLayout",
                    function() {
                      $Lightning.createComponent("c:LC01_GeneratePlainLayout",
                      {"WSName" : wsname,
                        "template" : template,
                        "abbreviations" : abbreviations,
                        "param1" : param1
                      },
                      "xi-div-container",
                      function(cmp) {
                        // do nothing for now, just create the component
                      });
                    });


  </script>
 </apex:page>

My lightning app :

 <aura:application access="GLOBAL" extends="ltng:outApp" >
     <ltng:require styles="{!$Resource.SLDS202_HV1 + '/assets/styles/salesforce-lightning-design-system.min.css'}"
                scripts="{!$Resource.GI_CustomHelpers + '/helper01.js'}" />
     <aura:dependency resource="c:LC01_GeneratePlainLayout"/>
 </aura:application>

My lightning cmp :

  <aura:component controller="LC01_WSCaller">  
    ......
    ......
    .......
   <div class="slds-button__icon xc-icon-bk-right" onclick="toggleSection(this,'x-card-1')"></div>

the toggleSection method is defined in my external js file. And for some reason I need to use the method defined in my js file, because div is generated dynamically, so i cannot , for now, use the controller action or helper methods. when i click on the button, I'm obtaining the following error message :

  Uncaught ReferenceError: toggleSection is not defined throws at https://xxxxprooffice--agsdev--c.cs5.visual.force.com/apex/VFP01_Display?tpname=Customer_Detail_Page_Template&wsname=RetrieveCustomerDetails&param1=0008227:1:1

but if I place the same method in my visualforce page in the script section, it works like a charm :

 .....................

 <script>
    var wsname = "{!$CurrentPage.parameters.wsname}"; // the webservice to be called
    var template = JSON.parse('{!strResult}');
    var abbreviations = JSON.parse('{!strAbbreviations}');        
    var param1 = "{!$CurrentPage.parameters.param1}"; // the parameter to pass to the ws

    $Lightning.use("c:LA01_GeneratePlainLayout",
                    function() {
                      $Lightning.createComponent("c:LC01_GeneratePlainLayout",
                      {"WSName" : wsname,
                        "template" : template,
                        "abbreviations" : abbreviations,
                        "param1" : param1
                      },
                      "xi-div-container",
                      function(cmp) {
                        // do nothing for now, just create the component
                      });
                    });

      function toggleSection(me, divid){
        ...........
        .............
        .............   
       }  
</script>

I do not want to place my js methods like this in my visualforce page. I want to be able to load a js file with my methods defined inside. How can i make it work??? Anyone with this problem ??

Update:

On chrome (developer mode), when I click on sources, in my helper01.js file, i have the following codes , with strict added :

function $globalEvalIIFE$(){with(arguments[1]||{}){with(arguments[0]||{}){return (function(window){
    "use strict";
     function hasClass(element, cls) {
       ...............
    }
    function toggleBetweenClasses(me,cls1,cls2){
      .........
    }
    function toggleSection(me, divid){
      ...............           
    }

}).call(arguments[0], arguments[0])}}}

If I add a method for testing purpose like this :

  window.toggleVisibility = function(eid){
       alert("element id is :" + eid);
    };

When i click on my button i got the following error :

Uncaught ReferenceError: toggleVisibility is not defined throws at
https://………………….

What am I doing wrong ?

Best Answer

Interesting - there are a few things going on here I think but let's first focus on the use of onclick= bound to a non aura client side controller - that should plain not work, ever. In aura markup what looks like a simple div is actually syntactic shorthand for something more like:

<aura:html tag="div" HTMLAttributes="{!bagOfAttributes}">

and in LC/Aura all event handlers must be bound to {!c.someHandler} expressions and not plain old JavaScript. If direct binding to a global plain old JavaScript function works I believe it's 100% unintentional and likely to be "fixed"/removed.

Second, this looks like LockerService's expected behavior inherited because all component JavaScript and any external JavaScript you require/import now runs with JavaScript strict mode enabled which cleans up many long standing global scope issues. In helper01.js are you publishing toggleSection() as a global variable? In other words simply declaring a function no longer makes it global - same is true for any variable/object in strict mode. You need to explicitly indicate that you want it added to global scope like so:

window.toggleSection = toggleSection;

You can of course directly assign window.toggleSection = function() { ... };

This is a strict mode standard and is not specific to LS.

Lastly, ltng:require is itself a Lightning component and is designed to be used inside of the component lifecycle - it's dynamically including your CSS and scripts much like RequireJS and other dynamic loaders do with a twist: it's integrated into the init/render flow. It removes most of the issues around dynamic loading (inherently asynchronous) and LC's component lifecycle but to get the benefits you'll need to work inside of the life cycle's constraints.