angularjs - nuevo - que es un modulo de angular
Inyectar módulo dinámicamente, solo si es necesario. (6)
El problema con las técnicas de carga diferida existentes es que hacen cosas que yo quiero hacer solo.
Por ejemplo, utilizando requirejs, me gustaría simplemente llamar:
require([''tinymce'', function() {
// here I would like to just have tinymce module loaded and working
});
Sin embargo, no funciona de esa manera. ¿Por qué? Según tengo entendido, AngularJS solo marca el módulo como ''para cargar en el futuro'', y si, por ejemplo, esperaré un poco, funcionará, podré usarlo. Así que en la función anterior me gustaría llamar a alguna función como loadPendingModules ();
En mi proyecto creé un proveedor simple (''lazyLoad'') que hace exactamente esto y nada más, así que ahora, si necesito tener algún módulo completamente cargado, puedo hacer lo siguiente:
myApp.controller(''myController'', [''$scope'', ''lazyLoad'', function($scope, lazyLoad) {
// ........
$scope.onMyButtonClicked = function() {
require([''tinymce'', function() {
lazyLoad.loadModules();
// and here I can work with the modules as they are completely loaded
}]);
};
// ........
});
Aquí hay un enlace al archivo fuente (licencia MPL): https://github.com/lessmarkup/less-markup/blob/master/LessMarkup/UserInterface/Scripts/Providers/lazyload.js
Estoy usando Require.js en combinación con Angular.js.
Algunos controladores necesitan enormes dependencias externas que otros no necesitan, por ejemplo, FirstController
requiere Angular UI Codemirror . Eso es un extra de 135 kb, al menos:
require([
"angular",
"angular.ui.codemirror" // requires codemirror itself
], function(angular) {
angular.module("app", [ ..., "ui.codemirror" ]).controller("FirstController", [ ... ]);
});
No quiero tener que incluir la directiva y la biblioteca Codemirror cada vez que mi página se cargue solo para hacer feliz a Angular.
Es por eso que en este momento estoy cargando el controlador solo cuando se golpea la ruta, como se hace aquí .
Sin embargo, cuando necesito algo como
define([
"app",
"angular.ui.codemirror"
], function(app) {
// ui-codemirror directive MUST be available to the view of this controller as of now
app.lazy.controller("FirstController", [
"$scope",
function($scope) {
// ...
}
]);
});
¿Cómo puedo indicarle a Angular que inyecte ui.codemirror
módulo ui.codemirror
(o cualquier otro módulo) en el módulo de la aplicación?
No me importa si es una forma pirata de lograr esto, a menos que implique modificar el código de las dependencias externas.
Si es útil: estoy corriendo Angular 1.2.0.
Hay una directiva que hará esto:
https://github.com/AndyGrom/loadOnDemand
ejemplo:
<div load-on-demand="''module_name''"></div>
He estado tratando de mezclar requirejs + Angular desde hace algún tiempo. Publiqué un pequeño proyecto en Github ( angular-require-lazy ) con mi esfuerzo hasta ahora, ya que el alcance es demasiado grande para el código en línea o los violines. El proyecto demuestra los siguientes puntos:
- Los módulos AngularJS están cargados perezosamente.
- Las directivas también pueden cargarse perezosamente.
- Hay un "módulo" de descubrimiento y un mecanismo de metadatos (vea mi otro proyecto favorito: require-lazy )
- La aplicación se divide en paquetes automáticamente (es decir, la construcción con r.js funciona)
Como se hace
- Los proveedores (por ejemplo,
$controllerProvider
,$compileProvider
) se capturan desde una función deconfig
(técnica que vi por primera vez en angularjs-requirejs-lazy-controllers ). - Después del arranque,
angular
es reemplazado por nuestro propio contenedor que puede manejar módulos cargados perezosamente. - El inyector es capturado y proporcionado como una promesa.
- Los módulos AMD se pueden convertir en módulos angulares.
Esta implementación satisface sus necesidades: puede cargar módulos angulares de forma lenta (al menos el ng-grid que estoy usando), es definitivamente pirata y no modifica bibliotecas externas.
Comentarios / opiniones son más que bienvenidos.
(EDITAR) La diferenciación de esta solución de otras es que no realiza llamadas de require()
dinámicas, por lo que puede construirse con r.js (y mi proyecto de requerimiento lento). Aparte de eso, las ideas son más o menos convergentes entre las distintas soluciones.
¡Buena suerte a todos!
La clave para esto es que cualquier módulo del que dependa su módulo de app
también debe ser un módulo de carga lenta. Esto se debe a que el proveedor y las cachés de instancia que los usos angulares de su servicio de $ inyector son privados y no exponen un método para registrar nuevos módulos una vez que se completa la inicialización.
Por lo tanto, la forma ''hacky'' de hacerlo sería editar cada uno de los módulos que desea cargar perezosamente para requerir un objeto de carga lenta (en el ejemplo que vinculó, el módulo se encuentra en el archivo ''appModules.js''), luego edite cada una de las llamadas de controlador, directiva, fábrica, etc. para usar app.lazy.{same call}
lugar.
Después de eso, puede continuar siguiendo el proyecto de muestra al que se ha vinculado observando cómo se cargan las rutas de las aplicaciones (el archivo ''appRoutes.js'' muestra cómo hacerlo).
No estoy muy seguro de si esto ayuda, pero buena suerte.
Te estoy enviando código de muestra. Está funcionando bien para mí. Así que por favor revise esto:
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
});
}])
Y en la página HTML:
<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>
Atención: use la solución de Nikos Paraskevopoulos, ya que es más confiable (lo estoy usando) y tiene muchos más ejemplos.
Bien, finalmente he descubierto cómo lograr esto con una breve ayuda con esta answer .
Como dije en mi pregunta, esto se ha convertido en una forma muy intrépida. Esto implica la aplicación de cada función en la matriz _invokeQueue
del módulo dependiente en el contexto del módulo de la aplicación.
Es algo como esto (presta más atención en la función moduleExtender, por favor):
define([ "angular" ], function( angular ) {
// Returns a angular module, searching for its name, if it''s a string
function get( name ) {
if ( typeof name === "string" ) {
return angular.module( name );
}
return name;
};
var moduleExtender = function( sourceModule ) {
var modules = Array.prototype.slice.call( arguments );
// Take sourceModule out of the array
modules.shift();
// Parse the source module
sourceModule = get( sourceModule );
if ( !sourceModule._amdDecorated ) {
throw new Error( "Can''t extend a module which hasn''t been decorated." );
}
// Merge all modules into the source module
modules.forEach(function( module ) {
module = get( module );
module._invokeQueue.reverse().forEach(function( call ) {
// call is in format [ provider, function, args ]
var provider = sourceModule._lazyProviders[ call[ 0 ] ];
// Same as for example $controllerProvider.register("Ctrl", function() { ... })
provider && provider[ call[ 1 ] ].apply( provider, call[ 2 ] );
});
});
};
var moduleDecorator = function( module ) {
module = get( module );
module.extend = moduleExtender.bind( null, module );
// Add config to decorate with lazy providers
module.config([
"$compileProvider",
"$controllerProvider",
"$filterProvider",
"$provide",
function( $compileProvider, $controllerProvider, $filterProvider, $provide ) {
module._lazyProviders = {
$compileProvider: $compileProvider,
$controllerProvider: $controllerProvider,
$filterProvider: $filterProvider,
$provide: $provide
};
module.lazy = {
// ...controller, directive, etc, all functions to define something in angular are here, just like the project mentioned in the question
};
module._amdDecorated = true;
}
]);
};
// Tadaaa, all done!
return {
decorate: moduleDecorator
};
});
Una vez hecho esto, solo necesito, por ejemplo, hacer esto:
app.extend( "ui.codemirror" ); // ui.codemirror module will now be available in my application
app.controller( "FirstController", [ ..., function() { });