angular-ui-router con requirejs, carga lenta del controlador

¿Podrías ayudarme a entender cómo cargar el controlador en el siguiente ejemplo antes de la vista? Parece que la vista se carga inmediatamente mientras el controlador aún no está cargado.

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

Creé un plunker de trabajo aquí.

Tengamos este 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

Observemos main.js – la configuración 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'] }); 

De hecho, solo creamos alias (rutas) para nuestros ControllerNames y sus archivos Controller_Scripts.js . Eso es. Además, volvemos a requerir la aplicación, pero en nuestro caso usaremos una función diferente más adelante para registrar los controladores cargados de forma lenta.

¿Qué significa el deps: ['app'] ? En primer lugar, debemos proporcionar el archivo app.js (la ‘aplicación’ significa encontrar app.js ) :

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

este valor devuelto es el que podemos pedir en cada archivo cargado de manera sincronizada

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

¿Cómo cargaremos los controladores perezosamente? Como ya se ha demostrado aquí para ngRoute

angularAMD v0.2.1

angularAMD es una utilidad que facilita el uso de RequireJS en aplicaciones AngularJS que soportan la carga bajo demanda de módulos de terceros como angular-ui.

Le pediremos a angular que haga referencia a $controllerProvider , y lo usaremos más tarde, para registrar los controladores.

Esta es la primera parte de nuestro 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; } ]); 

Como podemos ver, acabamos de crear la aplicación ‘app’ y también, el titular creado app_cached_providers (siguiendo el estilo angularAMD) . En la fase de configuración, pedimos angular para $controllerProvider y lo guardamos como referencia.

Ahora continuemos en 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; }], }, }); } ]);

Esta parte de arriba muestra dos estados de statement. Uno de ellos, 'home' es estándar, ninguno vago. Su controlador es implícito, pero estándar podría ser utilizado.

El segundo es el estado denominado "other" que hace objective a la vista sin nombre ui-view="" . Y aquí podemos ver primero, la carga perezosa. Dentro de la resolución (ver 🙂

Resolver

Puede usar resolver para proporcionarle a su controlador contenido o datos personalizados al estado. resolve es un mapa opcional de dependencias que debe ser inyectado en el controlador.

Si alguna de estas dependencias son promesas , se resolverán y convertirán a un valor antes de que se cree una instancia del controlador y se active el evento $ stateChangeSuccess.

Con eso en nuestra suite, sabemos que el controlador (por su nombre) se buscará en el repository angular una vez que la resolución haya finalizado:

 // 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; }], }, 

Bueno, ahora, como se mencionó anteriormente, el principal contiene este alias def

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

Y eso significa que se buscará y cargará el archivo “Controller_Other.js”. Este es su contenido que hace la magia . El más importante aquí es el uso de una referencia previamente almacenada en caché a $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"; }); }); 

el truco es no usar app.controller() pero

$controllerProvider.Register

Angular usa el servicio $ controller para crear nuevos controladores. Este proveedor permite el registro del controlador a través del método register() .

Finalmente, hay otra definición de estado, con una resolución más estrecha … un bash de hacerlo más legible:

 // 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); }]);

Arriba podemos ver que resolvemos dos controladores para ambas / todas las vistas nombradas de ese estado

Eso es. Cada controlador definido aquí

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

se cargará a través de resolve y $controllerProvider – a través de RequireJS – de forma perezosa. Comprueba que todo aquí

Preguntas y respuestas similares: AngularAMD + ui-router + nombre de controlador dynamic?

En un proyecto utilicé la carga de controladores perezosos y tuve que llamar manualmente un $ digest en el scope para que funcione. Supongo que este comportamiento no cambia con el enrutador ui. ¿Lo intentaste?

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