javascript - otro - AngularJS: controladores de carga lenta y contenido
llamar funcion de otro controlador angularjs (9)
En este escenario simplificado, tengo dos archivos: 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>
lazy.htm
myApp.controller(''lazy'',function($scope){
$scope.lazy = ''Lazy Controller'';
});
<div ng-controller="lazy">
{{lazy}}
</div>
El resultado es un error: "El argumento ''perezoso'' no es una función, se ha indefinido"
Usando una función en su lugar
lazy.htm
function lazy($scope) {
$scope.lazy = ''Lazy Controller'';
}
<div ng-controller="lazy">
{{lazy}}
</div>
Esto funciona hasta la versión 1.3 beta 14. En la versión beta 15 se eliminaron las funciones del controlador global: https://github.com/angular/angular.js/issues/8296
Entonces, ¿cuál es la mejor manera de obtener dinámicamente los contenidos angulosos de lazy.htm?
ACTUALIZAR:
En este artículo ( http://ify.io/lazy-loading-in-angularjs ) encontré otra solución posible. El $ controllerProvider nos permite registrar nuevos controladores después de un arranque angular. Funciona de maravilla. Probado en 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>
lazy.htm
myApp.cp.register(''lazy'',function($scope){
$scope.lazy = ''Lazy Controller'';
});
<div ng-controller="lazy">
{{lazy}}
</div>
ACTUALIZACIÓN 2:
Otras dos alternativas que funcionan son:
lazy.htm
_app = $(''[ng-app]'').scope();
_app.lazy = function($scope) {
$scope.lazy = ''Lazy Controller'';
};
O
var $rootScope = $(''[ng-app]'').injector().get(''$rootScope'');
$rootScope.lazy = function($scope) {
$scope.lazy = ''Lazy Controller'';
};
Pero creo que estos dos últimos ejemplos no deberían usarse en la producción.
¡También puedes usar directivas para cargar tu controlador!
Un ejemplo aquí:
//// archivo 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
}
// declaración de contoller
angularApp.components.controller(''DiscussionController'',[function(){
}]);
Al principio utilicé la respuesta de André Betiolo. Sin embargo, no siempre funciona porque la carga de ajax no es de bloqueo, lo que hace que la vista a veces solicite el controlador antes de que se cargue el script.
Como solución, forcé a la función a no regresar hasta que todos los scripts se cargaran correctamente. Esto es una especie de piratería, pero se asegura de que las cargas sean exitosas antes de completar la resolución. También permite la carga de múltiples controladores.
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'';
});
Idealmente: Angular lo obligará a separar HTML y JS, ya que en las versiones más recientes, esto puede aplicarse con más frecuencia.
Es posible que tenga que utilizar requireJS http://solutionoptimist.com/2013/09/30/requirejs-angularjs-dependency-injection/
Por el bien del truco puedes intentarlo
ng-controller-controller="''lazy''"
o
En HTML
ng-controller-controller = "myObject.controller"
En algún lugar inyectar
$scope.myObject.controller = $controller(''lazy'', {$scope: $scope})
La mejor manera de hacer lo que está pidiendo es, en cambio, usar una directiva y unir el controlador y la plantilla de esa manera, de modo que esté vinculado en el momento adecuado. Actualmente, el enlace no ocurre en lazy.htm
en el momento adecuado a menos que declare una función global como se muestra en el segundo ejemplo.
Prueba este plugin ARI para Angular JS. Te ayuda a cargar de forma perezosa los scripts del controlador a pedido.
También puede usar el jquery con la resolución $ 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'';
});
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>
Gracias
Usted puede tener la carga perezosa AngularJS puro.
Crear "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;
}
}
}]);
Luego, define tu configuración:
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'');
}]
}
});
. . .
En "js / services / vendor.js", cree el servicio como:
var ng = angular.module(''app'');
ng.lazy.service(''vendorService'', [ function() {
. . .
En "js / controllers / vendor.js", cree el controlador como:
var ng = angular.module(''app'');
ng.lazy.controller(''vendorController'', [ function() {
. . .
La propiedad "resolver" en cuándo define qué promesas deben resolverse antes de que se carguen las rutas.