How to Lazy load non-angular JavaScript files using AngularJS?

后端 未结 3 1065
深忆病人
深忆病人 2020-12-15 21:43

Is it possible to load plain old JS or AMD modules from an Angular Controller? I have previously used RequireJS for this.

I have used AngularJS and RequireJS on a fa

相关标签:
3条回答
  • 2020-12-15 21:55

    Actually i don't know much about angular's directives and it's modular things, but with my basic knowledge i build some preloading function for loading my JS files.... loadScript function is defined in my app.js which is included in main html page.

    function loadScript(src,callback){
    
        var script = document.createElement("script");
        script.type = "text/javascript";
        if(callback)script.onload=callback;
        document.getElementsByTagName("head")[0].appendChild(script);
        script.src = src;
    }
    

    inside controller use something like this

    app.controller('myCtrl', function($scope) {
      //array of things to load are saved in a JavascriptFile
      loadScript("URL_To_JavascriptFile");
    });
    
    0 讨论(0)
  • 2020-12-15 21:55

    Angular's module system is not like RequireJS. Angular only concerns itself with module definition and composition but not with module loading.

    That means angular will not issue an HTTP request for loading any javascript, you are responsible for loading that yourself, by using <script> tags or another javascript loader, like requirejs.

    Furthermore, angular requires that all modules be loaded and configured before the angular application is bootstrapped. This makes it difficult to support any sort of lazy-loading schemes for angular modules.

    Making angular be able to deal with more dynamic loading of modules is a featured planned for future releases.

    0 讨论(0)
  • 2020-12-15 21:56

    Ok, I commented my code so that it should explain everything. If you have any further questions, just let me know. This is the solution to the issues as are further explained in your comments. Live demo here (click).

    Markup:

    <!DOCTYPE html>
    <html ng-app="myApp" ng-controller="myCtrl">
    <head>
    </head>
    <body>
    
      <button ng-click="load()">Load Foo</button>
      <!-- I'm using this to bootstrap the lazy loaded script -->
      <section ng-repeat="item in loaded" lazy="item"></section>
    
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>  
    <script src="script.js"></script>
    </body>
    </html>
    

    JavaScript:

    var app = angular.module('myApp', []);
    
    app.controller('myCtrl', function($scope) {
      //array of things to load
      $scope.lazyThings = [
        {directive:'my-foo-directive', file:'foo.js'}  
      ];
      $scope.loaded = [];
      $scope.load = function() {
        var loadIndex = $scope.loaded.length;
        if ($scope.lazyThings[loadIndex]) {
          $scope.loaded.push($scope.lazyThings[loadIndex]);
        }
      }
    });
    
    app.factory('myService', function($http) {
      return {
        getJs: function(path) {
    
          return $http.get(path).then(function(response) {
            deferred.resolve(response.data);
          });
    
        }
      }
    });
    
    //this adds an attribute to kick off the directive 
    //for whatever script was lazy loaded
    app.directive('lazy', function($compile, $q, myService) {
      var directiveReturn = {
        restrict: 'A',
        scope: {
          lazy: '='
        },
        link: function(scope, element) {
          myService.getJs(scope.lazy.file).then(function(data) {
            return addScript(scope.lazy.file, data, scope);
          }).then(function() {
            var $span = angular.element('<span></span>').attr(scope.lazy.directive, '');
            $compile($span)(scope);
            element.append($span);
          });
        }
      }
    
      var scriptPromises = {};
      function addScript(file, js, scope) {
        if (!scriptPromises[file]) { //if this controller hasn't already been loaded
          var deferred = $q.defer();
          //cache promise)
          scriptPromises[file] = deferred.promise;
    
          //inject js into a script tag
          var script = document.createElement('script');
          script.src = 'data:text/javascript,' + encodeURI(js);
          script.onload = function() {
            //now the script is ready for use, resolve promise to add the script's directive element
            scope.$apply(deferred.resolve());
          };
          document.body.appendChild(script);
          return deferred.promise;
        }
        else { //this script has been loaded before
          return scriptPromises[loadFile]; //return the resolved promise from cache
        }
      }
    
      return directiveReturn;
    });
    
    app.directive('myFooDirective', function() {
      return {
        restrict: 'A',
        link: function(scope, element) {
          //put the logic from your lazy loaded "foo.js" script here
          element.text(foo.someFunction());
        }
      }
    });
    

    Sample lazy loaded script:

    var foo = {
      someFunction: function() {
        return 'I am data from a lazy loaded js function!';
      }
    };
    

    There are plenty of ways that you could implement the concept I demonstrated here. Just think about how you would like to use it, and write some directives to carry it out. Angular makes most things pretty simple.

    Note: Injecting the script tag is optional - but I greatly prefer that rather than executing the script directly. When using the script tag, you will be able to track all of the loaded files with the dev tools under "Resources" in the "Scripts" section.

    0 讨论(0)
提交回复
热议问题