angular-ui-router con requirejs, caricamento lento del controller

Potresti aiutarmi a capire come caricare il controller nell’esempio qui sotto prima della visualizzazione? Sembra che la vista sia caricata immediatamente mentre il controller non è ancora stato caricato.

//app.js $stateProvider.state('index', { url: "/", views: { "topMenu": { templateUrl: "/Home/TopMenu", controller: function($scope, $injector) { require(['controllers/top-menu-controller'], function(module) { $injector.invoke(module, this, { '$scope': $scope }); }); } } } }); //top-menu-controller.js define(['app'], function (app) { app.controller('TopMenuCtrl', ['$scope', function ($scope) { $scope.message = "It works"; }]); }); //Home/TopMenu 

TopMenu

{{message}}

Ho creato un plunker funzionante qui.

Facciamo questo index.html :

    my lazy   #/home // we have three states - 'home' is NOT lazy #/ - index // 'index' is lazy, with two views #/other // 'other' is lazy with unnamed view 
// standard angular // and ui-router scritps // our application

Osserviamo il main.js – la configurazione di RequireJS:

 require.config({ //baseUrl: "js/scripts", baseUrl: "", // alias libraries paths paths: { // here we define path to NAMES // to make controllers and their lazy-file-names independent "TopMenuCtrl": "Controller_TopMenu", "ContentCtrl": "Controller_Content", "OtherCtrl" : "Controller_Other", }, deps: ['app'] }); 

In effetti, creiamo solo alias (percorsi) per i nostri ControllerNames e i loro file Controller_Scripts.js . Questo è tutto. Inoltre, torniamo a richiedere l’app, ma nel nostro caso utilizzeremo funzionalità diverse in seguito, per registrare i controller caricati pigramente.

cosa significa ” deps: ['app'] ? In primo luogo, dobbiamo fornire il file app.js (l”app’ indica find app.js ) :

 define([], function() { var app = angular.module('app'); return app; }) 

questo valore restituito è quello che possiamo chiedere in ogni file caricato asincrono

 define(['app'], function (app) { // here we would have access to the module("app") }); 

Come caricheremo pigramente i controller? Come già dimostrato qui per ngRoute

angularAMD v0.2.1

angularAMD è un’utilità che facilita l’uso di RequireJS in applicazioni AngularJS che supportano il caricamento su richiesta di moduli di terze parti come angular-ui.

Chiederemo ad angular un riferimento a $controllerProvider – e lo useremo in seguito, per registrare i controller.

Questa è la prima parte del nostro script.js :

 // I. the application var app = angular.module('app', [ "ui.router" ]); // II. cached $controllerProvider var app_cached_providers = {}; app.config(['$controllerProvider', function(controllerProvider) { app_cached_providers.$controllerProvider = controllerProvider; } ]); 

Come possiamo vedere, abbiamo appena creato l’applicazione ‘app’ e inoltre, creato titolare app_cached_providers (seguendo lo stile angularAMD) . Nella fase di configurazione, chiediamo ad angular per $controllerProvider e mantenere il riferimento per esso.

Ora continuiamo con script.js :

 // III. inline dependency expression app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) { $urlRouterProvider .otherwise("/home"); $stateProvider .state("home", { url: "/home", template: "
this is home - not lazily loaded
" }); $stateProvider .state("other", { url: "/other", template: "
The message from ctrl: {{message}}
", controller: "OtherCtrl", resolve: { loadOtherCtrl: ["$q", function($q) { var deferred = $q.defer(); require(["OtherCtrl"], function() { deferred.resolve(); }); return deferred.promise; }], }, }); } ]);

Questa parte in alto mostra la dichiarazione di due stati. Uno di loro – 'home' è di serie nessuno pigro. Il controller è implicito, ma potrebbe essere utilizzato lo standard.

Il secondo è stato denominato "other" che mira alla vista senza nome ui-view="" . E qui possiamo prima vedere, il carico pigro. All’interno della risoluzione (vedi 🙂

Risolvere

È ansible utilizzare la risoluzione per fornire al controller contenuto o dati personalizzati per lo stato. resolve è una mappa opzionale di dipendenze che dovrebbe essere iniettata nel controller.

Se una qualsiasi di queste dipendenze è promise , verrà risolta e convertita in un valore prima dell’istanza del controller e verrà generato l’evento $ stateChangeSuccess.

Con quello nella nostra suite, sappiamo, che il controller (con il suo nome) verrà cercato nel repository angular una volta che la risoluzione è terminata:

 // this controller name will be searched - only once the resolve is finished controller: "OtherCtrl", // let's ask RequireJS resolve: { loadOtherCtrl: ["$q", function($q) { // wee need $q to wait var deferred = $q.defer(); // and make it resolved once require will load the file require(["OtherCtrl"], function() { deferred.resolve(); }); return deferred.promise; }], }, 

Bene, ora, come detto sopra, il principale contiene questo alias def

 // alias libraries paths paths: { ... "OtherCtrl" : "Controller_Other", 

Ciò significa che il file “Controller_Other.js” verrà cercato e caricato. Questo è il suo contenuto che fa la magia . Il più importante qui è l’utilizzo del riferimento precedentemente memorizzato nella cache in $controllerProvider

 // content of the "Controller_Other.js" define(['app'], function (app) { // the Default Controller // is added into the 'app' module // lazily, and only once app_cached_providers .$controllerProvider .register('OtherCtrl', function ($scope) { $scope.message = "OtherCtrl"; }); }); 

il trucco non è usare app.controller() ma

$controllerProvider.Register

Il servizio $ controller viene utilizzato da Angular per creare nuovi controller. Questo provider consente la registrazione del controllore tramite il metodo register() .

Infine c’è un’altra definizione di stato, con una risoluzione più ristretta … un tentativo di renderlo più leggibile:

 // IV ... build the object with helper functions // then assign to state provider var loadController = function(controllerName) { return ["$q", function($q) { var deferred = $q.defer(); require([controllerName], function() {deferred.resolve(); }); return deferred.promise; }]; } app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) { var index = { url: "/", views: { "topMenu": { template: "
The message from ctrl: {{message}}
", controller: "TopMenuCtrl", }, "": { template: "
The message from ctrl: {{message}}
", controller: "ContentCtrl", }, }, resolve : { }, }; index.resolve.loadTopMenuCtrl = loadController("TopMenuCtrl"); index.resolve.loadContentCtrl = loadController("ContentCtrl"); $stateProvider .state("index", index); }]);

Qui sopra possiamo vedere che risolviamo due controller per entrambe le viste / tutti i nomi di quello stato

Questo è tutto. Ogni controller definito qui

 paths: { "TopMenuCtrl": "Controller_TopMenu", "ContentCtrl": "Controller_Content", "OtherCtrl" : "Controller_Other", ... }, 

verrà caricato tramite risoluzione e $controllerProvider – via RequireJS – pigramente. Controlla tutto qui

Domande e risposte simili: AngularAMD + ui-router + nome del controller dinamico?

Su un progetto ho usato il caricamento pigro dei controller e ho dovuto chiamare manualmente un $ digest sull’oscilloscopio per farlo funzionare. Immagino che questo comportamento non cambi con ui-router. Ci hai provato?

 define(['app'], function (app) { app.controller('TopMenuCtrl', ['$scope', function ($scope) { $scope.message = "It works"; $scope.$digest(); }]); });