web-dev-qa-db-fra.com

Comment charger paresseux des fichiers JavaScript non angulaires avec AngularJS?

Est-il possible de charger d'anciens modules simples JS ou AMD à partir d'un contrôleur Angular? J'ai déjà utilisé RequireJS pour cela.

J'ai déjà utilisé AngularJS et RequireJS sur un projet assez volumineux. Je travaille sur un nouveau projet basé sur la graine MEAN Stack, qui n’utilise pas requireJS.

Je ne suis pas tout à fait clair, mais Angular a un système de chargement de modules. Puis-je charger un élément particulier de javascript à partir de mon contrôleur angulaire?

Est-il possible de modifier ma déclaration module () pour inclure des fichiers javascript réguliers supplémentaires?

Merci!

EDIT: Pour donner un peu de compréhension sur ce que je fais, j'ai une page qui édite quelques formulaires différents. Chacun de ceux-ci est enregistré dans ma base de données en tant que "formulaire". Selon le type de formulaire, différentes valeurs de dictionnaire sont associées à différents champs dans différentes sous-vues. Certains de mes formulaires comportent par exemple des listes déroulantes ou des listes d’entrées. Celles-ci sont différentes, mais tout le reste de la "fiche" est traité de manière générique.

J'ai donc ce contrôleur de formulaire singulier qui gère un grand nombre de formulaires différents et je suis très heureux du résultat. Le problème principal vient du fait que chaque formulaire contient un ensemble de données que j'aimerais éviter de charger, sauf si j'en ai besoin.

Je peux vérifier quel formulaire je charge en consultant la liste déroulante qui commande mon ng-include (qui charge le sous-formulaire).

À court terme, je viens de tout charger et de créer des espaces de noms pour me différencier.

par exemple, $ scope.form1 et $ scope.form2 pour les données/règles spécifiques à un formulaire particulier. Je préférerais aussi ne pas charger les js dont je n'ai pas besoin.

Éditer 2: http://jsfiddle.net/HB7LU/1320/

function MyCtrl($scope) {       
    $scope.doSomething = function()
    {
     //I'm used to wrapping with e.g require('jquery..... here, can I do the same thing with angular?   
        alert(" I need to run a jquery function here...");
        var xml = $(someblock);
    };
}

J'ai monté un violon avec exactement ce dont je parle. Je souhaite charger des fichiers JS arbitraires en fonction de certains chemins dans mon contrôleur et uniquement lorsque j'en ai besoin.

Fondamentalement, je souhaite charger des espaces de noms plus volumineux en fonction de l'une des nombreuses options sélectionnées et il serait coûteux de les charger tous.

16
Yablargo

Ok, j'ai commenté mon code pour qu'il explique tout. Si vous avez d'autres questions, faites le moi savoir. C'est la solution aux problèmes, comme expliqué plus en détail dans vos commentaires. Live démo ici (cliquez).

Balisage:

<!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());
    }
  }
});

Exemple de script chargé paresseux:

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

Vous pouvez utiliser le concept que j'ai présenté ici de nombreuses façons. Réfléchissez simplement à la façon dont vous voudriez l’utiliser et écrivez quelques directives pour l’exécuter. Angular simplifie la plupart des choses.

Remarque: L'injection de la balise de script est facultative, mais je la préfère grandement, plutôt que d'exécuter le script directement. Lorsque vous utilisez la balise de script, vous pourrez suivre tous les fichiers chargés avec les outils de développement sous "Ressources" dans la section "Scripts".

19
m59

En fait, je ne connais pas grand chose aux directives angular et aux choses modulaires, mais avec mes connaissances de base, je crée une fonction de préchargement pour charger mes fichiers JS .... inclus dans la page html principale.

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;
}

contrôleur intérieur utilise quelque chose comme ça

app.controller('myCtrl', function($scope) {
  //array of things to load are saved in a JavascriptFile
  loadScript("URL_To_JavascriptFile");
});
8
Sooraj ER

Le système de modules d'Angular ne ressemble pas à RequireJS. Angular ne concerne que la définition et la composition du module, mais pas le chargement du module. 

Cela signifie qu'angular n'émettra pas de requête HTTP pour le chargement de javascript, vous êtes responsable de le faire vous-même, en utilisant des balises <script> ou un autre chargeur javascript, comme requirejs.

De plus, angular exige que tous les modules soient chargés et configurés avant le démarrage de l'application angulaire. Cela rend difficile la prise en charge de tout type de système de chargement paresseux pour les modules angulaires. 

Faire en sorte que angular soit capable de gérer un chargement plus dynamique des modules est prévu pour les versions futures.

1
dtabuenc