AngularJS: contrôleurs et contenu de chargement paresseux

Dans ce scénario simplifié, j'ai deux fichiers: index.htm, lazy.htm.

Index.htm:

var myApp = angular.module('myApp', []); myApp.controller('embed',function($scope){ $scope.embed = 'Embedded Controller'; }); <div ng-controller="embed">{{embed}}</div> <div ng-include="'lazy.htm'"></div> 

Paresseux.htm

 myApp.controller('lazy',function($scope){ $scope.lazy = 'Lazy Controller'; }); <div ng-controller="lazy"> {{lazy}} </div> 

Le résultat est une erreur: "Argument 'paresseux' n'est pas une fonction, n'a pas défini"

Utilisation à la place d'une fonction

Paresseux.htm

 function lazy($scope) { $scope.lazy = 'Lazy Controller'; } <div ng-controller="lazy"> {{lazy}} </div> 

Cela fonctionne jusqu'à la version 1.3 beta 14. Dans la version beta 15, les fonctions du contrôleur global ont été supprimées: https://github.com/angular/angular.js/issues/8296

Alors maintenant, quelle est la meilleure façon d'obtenir des contenus angularisés de lazy.htm de façon dynamique?

METTRE À JOUR:

Dans cet article ( http://ify.io/lazy-loading-in-angularjs ), j'ai trouvé une autre solution possible. Le $ controllerProvider nous permet d'enregistrer de nouveaux contrôleurs après le démarrage angulaire. Fonctionne comme un charme. Testé dans v1.3.0-beta.18

Index.htm:

 var myApp = angular.module('myApp', []) .controller('embed',function($scope){ $scope.embed = 'Embedded Controller'; }) .config(function($controllerProvider) { myApp.cp = $controllerProvider; }); <div ng-controller="embed">{{embed}}</div> <div ng-include="'lazy.htm'"></div> 

Paresseux.htm

 myApp.cp.register('lazy',function($scope){ $scope.lazy = 'Lazy Controller'; }); <div ng-controller="lazy"> {{lazy}} </div> 

MISE À JOUR 2:

Deux autres alternatives qui fonctionnent sont:

Paresseux.htm

 _app = $('[ng-app]').scope(); _app.lazy = function($scope) { $scope.lazy = 'Lazy Controller'; }; 

OU

 var $rootScope = $('[ng-app]').injector().get('$rootScope'); $rootScope.lazy = function($scope) { $scope.lazy = 'Lazy Controller'; }; 

Mais je crois que ces deux derniers exemples ne devraient pas être utilisés dans la production.

Vous pouvez également utiliser la jquery avec la solution pour $ routeProvider

App.js

 /* Module Creation */ var app = angular.module ('app', ['ngRoute']); app.config(['$routeProvider', '$controllerProvider', function($routeProvider, $controllerProvider){ /*Creating a more synthesized form of service of $ controllerProvider.register*/ app.registerCtrl = $controllerProvider.register; function loadScript(path) { var result = $.Deferred(), script = document.createElement("script"); script.async = "async"; script.type = "text/javascript"; script.src = path; script.onload = script.onreadystatechange = function (_, isAbort) { if (!script.readyState || /loaded|complete/.test(script.readyState)) { if (isAbort) result.reject(); else result.resolve(); } }; script.onerror = function () { result.reject(); }; document.querySelector("head").appendChild(script); return result.promise(); } function loader(arrayName){ return { load: function($q){ var deferred = $q.defer(), map = arrayName.map(function(name) { return loadScript('js/controllers/'+name+".js"); }); $q.all(map).then(function(r){ deferred.resolve(); }); return deferred.promise; } }; } $routeProvider .when('/', { templateUrl: 'views/foo.html', resolve: loader(['foo']) }) .when('/bar',{ templateUrl: 'views/bar.html', controller: 'BarCtrl', resolve: loader(['bar']) }) .otherwise({ redirectTo: document.location.pathname }); }]); 

/views/foo.html

 <section ng-controller='FooCtrl'> {{text}} </section> 

Js / controllers / foo.js

 /*Here we use the synthesized version of $controllerProvider.register to register the controller in view*/ app.registerCtrl('FooCtrl',function($scope){ $scope.text = 'Test'; }); 

/views/bar.html

 <section> {{text2}} </section> 

Js / controllers / bar.js

 app.registerCtrl('BarCtrl',function($scope){ $scope.text2 = 'Test'; }); 

//// Fichier JConfig ——–

 window.angularApp.config(function ($routeProvider,$controllerProvider,$compileProvider,$provide, azMessages) { $routeProvider.when('/login', { resolve: { load: ['$q', '$rootScope', function ($q, $rootScope) { var deferred = $q.defer(); require([ //load required Js file here ], function () { $rootScope.$apply(function () { deferred.resolve(); }); }); return deferred.promise; } ] } }); $routeProvider.otherwise({ redirectTo: '/login' }); window.angularApp.components = { controller: $controllerProvider.register, service: $provide.service, directive: $compileProvider.directive } 

// déclaration contoller

 angularApp.components.controller('DiscussionController',[function(){ }]); 

Au début, j'ai utilisé la réponse d'André Betiolo. Cependant, cela ne fonctionne pas toujours parce que le chargement d'ajax est non bloquant, ce qui provoque parfois la demande du contrôleur avant le chargement du script.

En tant que solution, j'ai forcé la fonction à ne pas revenir jusqu'à ce que tous les scripts soient chargés avec succès. C'est une sorte de hack, mais veille à ce que les charges réussissent avant de terminer la résolution. Il permet également le chargement de plusieurs contrôleurs.

App.js

 var app = angular.module ('app', ['ngRoute']); app.config(['$routeProvider', '$controllerProvider', function($routeProvider, $controllerProvider){ /*Creating a more synthesized form of service of $ controllerProvider.register*/ app.registerCtrl = $controllerProvider.register; //jquery to dynamically include controllers as needed function controllers(controllers){ var numLoaded = 0; for (i = 0; i < controllers.length; i++) { $.ajaxSetup({async:false}); $.getScript('js/controllers/' + controllers[i] + '.js').success(function(){ numLoaded++; if (numLoaded == controllers.length) { return true; //only return after all scripts are loaded, this is blocking, and will fail if all scripts aren't loaded. } }); } } $routeProvider .when('/', { templateUrl: 'views/foo.html', resolve: { load: function () { controllers(['foo']) } } }) .when('/bar',{ templateUrl: 'views/bar.html', controller: 'BarCtrl', resolve: { load: function () { controllers(['bar','foo']) //you can load multiple controller files } } }) .otherwise({ redirectTo: document.location.pathname }); }]); 

/views/foo.html

 <section ng-controller='FooCtrl'> {{text}} </section> 

/views/bar.html

 <section ng-controller='BarCtrl'> {{text2}} </section> <section ng-controller='FooCtrl'> {{text}} </section> 

/controllers/bar.js

 app.registerCtrl('BarCtrl',function($scope){ $scope.text2 = 'Test'; }); 

Vous pouvez avoir un pur chargement par AngularJS.

Créer "LazyService":

 var ng = angular.module('app'); ng.factory('lazyService', [ '$http', function($http) { var jsPath = 'js/${ name }.js'; var promisesCache = {}; return { loadScript: function(name) { var path = jsPath.replace('${ name }', name); var promise = promisesCache[name]; if (!promise) { promise = $http.get(path); promisesCache[name] = promise; return promise.then(function(result) { eval(result.data); console.info('Loaded: ' + path); }); } return promise; } } }]); 

Ensuite, définissez votre configuration:

 var ng = angular.module('app', [ 'ngRoute' ]); ng.config([ '$routeProvider', '$controllerProvider', '$provide', function($routeProvider, $controllerProvider, $provide) { // Lazy loading ng.lazy = { controller: $controllerProvider.register, //directive: $compileProvider.directive, //filter: $filterProvider.register, factory: $provide.factory, service: $provide.service } $routeProvider .when('/', { templateUrl: 'view/home.html' }) .when('/vendor', { templateUrl: 'view/vendor.html', resolve: { svc: [ 'lazyService', function(lazyService) { return lazyService.loadScript('services/vendor'); }], ctrl: [ 'lazyService', function(lazyService) { return lazyService.loadScript('controllers/vendor'); }] } }); . . . 

Sur "js / services / vendor.js", créez le service comme suit:

 var ng = angular.module('app'); ng.lazy.service('vendorService', [ function() { . . . 

Sur "js / controllers / vendor.js", créez le contrôleur comme suit:

 var ng = angular.module('app'); ng.lazy.controller('vendorController', [ function() { . . . 

La propriété "résoudre" s'affiche lorsque définit les promesses qui doivent être résolues avant que la route ne soit chargée.

La meilleure façon de faire ce que vous demandez est d'utiliser plutôt une directive et de lier le contrôleur et le modèle de manière telle qu'il soit lié au moment approprié. Actuellement, la liaison ne se passe pas dans lazy.htm au bon moment, sauf si vous déclarez une fonction globale comme vous l'avez montré dans votre deuxième exemple.

Idéalement – Angular vous obligera à séparer HTML et JS comme dans les versions plus récentes, cela peut être appliqué plus souvent.

Vous devrez peut-être utiliser requireJS http://solutionoptimist.com/2013/09/30/requirejs-angularjs-dependency-injection/

Pour l'amour, pouvez-vous essayer

 ng-controller-controller="'lazy'" 

ou

En HTML

Ng-controller-controller = "myObject.controller"

Quelque part injecte

 $scope.myObject.controller = $controller('lazy', {$scope: $scope}) 

Essayez ce plugin ARI pour Angular JS. Il vous aide à charger avec précaution les scripts du contrôleur à la demande.

Vous pouvez également utiliser les directives pour charger votre contrôleur!

Un exemple ici:

https://gist.github.com/raphaelluchini/53d08ed1331e47aa6a87

Je vous envoie un exemple de code. Cela fonctionne bien pour moi. Alors, vérifiez ceci:

 var myapp = angular.module('myapp', ['ngRoute']); /* Module Creation */ var app = angular.module('app', ['ngRoute']); app.config(['$routeProvider', '$controllerProvider', function ($routeProvider, $controllerProvider) { app.register = { controller: $controllerProvider.register, //directive: $compileProvider.directive, //filter: $filterProvider.register, //factory: $provide.factory, //service: $provide.service }; // so I keep a reference from when I ran my module config function registerController(moduleName, controllerName) { // Here I cannot get the controller function directly so I // need to loop through the module's _invokeQueue to get it var queue = angular.module(moduleName)._invokeQueue; for (var i = 0; i < queue.length; i++) { var call = queue[i]; if (call[0] == "$controllerProvider" && call[1] == "register" && call[2][0] == controllerName) { app.register.controller(controllerName, call[2][1]); } } } var tt = { loadScript: function (path) { var result = $.Deferred(), script = document.createElement("script"); script.async = "async"; script.type = "text/javascript"; script.src = path; script.onload = script.onreadystatechange = function (_, isAbort) { if (!script.readyState || /loaded|complete/.test(script.readyState)) { if (isAbort) result.reject(); else { result.resolve(); } } }; script.onerror = function () { result.reject(); }; document.querySelector(".shubham").appendChild(script); return result.promise(); } } function stripScripts(s) { var div = document.querySelector(".shubham"); div.innerHTML = s; var scripts = div.getElementsByTagName('script'); var i = scripts.length; while (i--) { scripts[i].parentNode.removeChild(scripts[i]); } return div.innerHTML; } function loader(arrayName) { return { load: function ($q) { stripScripts(''); // This Function Remove javascript from Local var deferred = $q.defer(), map = arrayName.map(function (obj) { return tt.loadScript(obj.path) .then(function () { registerController(obj.module, obj.controller); }) }); $q.all(map).then(function (r) { deferred.resolve(); }); return deferred.promise; } }; }; $routeProvider .when('/first', { templateUrl: '/Views/foo.html', resolve: loader([{ controller: 'FirstController', path: '/MyScripts/FirstController.js', module: 'app' }, { controller: 'SecondController', path: '/MyScripts/SecondController.js', module: 'app' }]) }) .when('/second', { templateUrl: '/Views/bar.html', resolve: loader([{ controller: 'SecondController', path: '/MyScripts/SecondController.js', module: 'app' }, { controller: 'A', path: '/MyScripts/anotherModuleController.js', module: 'myapp' }]) }) .otherwise({ redirectTo: document.location.pathname }); }]) 

Et en HTML Page:

 <body ng-app="app"> <div class="container example"> <!--ng-controller="testController"--> <h3>Hello</h3> <table> <tr> <td><a href="#/first">First Page </a></td> <td><a href="#/second">Second Page</a></td> </tr> </table> <div id="ng-view" class="wrapper_inside" ng-view> </div> <div class="shubham"> </div> </div> 

Merci