AngularJS:延迟加载控制器和内容
Posted
技术标签:
【中文标题】AngularJS:延迟加载控制器和内容【英文标题】:AngularJS: lazy loading controllers and content 【发布时间】:2014-08-06 19:28:35 【问题描述】:在这个简化的场景中,我有两个文件: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>
懒惰的.htm
myApp.controller('lazy',function($scope)
$scope.lazy = 'Lazy Controller';
);
<div ng-controller="lazy">
lazy
</div>
结果是错误:“Argument 'lazy' is not a function, got undefined”
使用函数代替
懒惰的.htm
function lazy($scope)
$scope.lazy = 'Lazy Controller';
<div ng-controller="lazy">
lazy
</div>
这在 1.3 beta 14 之前有效。在 beta 15 中删除了全局控制器功能:https://github.com/angular/angular.js/issues/8296
那么现在,动态获取lazy.htm 的角度化内容的更好方法是什么?
更新:
在这篇文章 (http://ify.io/lazy-loading-in-angularjs) 中,我找到了另一种可能的解决方案。 $controllerProvider 允许我们在 angular bootstrap 之后注册新的控制器。奇迹般有效。在 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>
懒惰的.htm
myApp.cp.register('lazy',function($scope)
$scope.lazy = 'Lazy Controller';
);
<div ng-controller="lazy">
lazy
</div>
更新 2:
另外两个可行的选择是:
懒惰的.htm
_app = $('[ng-app]').scope();
_app.lazy = function($scope)
$scope.lazy = 'Lazy Controller';
;
或
var $rootScope = $('[ng-app]').injector().get('$rootScope');
$rootScope.lazy = function($scope)
$scope.lazy = 'Lazy Controller';
;
但我认为最后两个示例不应在生产中使用。
【问题讨论】:
我还发现 requirejs 很难与其他库一起使用,反之亦然。这就是为什么我创建了一个更易于使用并使用 Angular 进行测试的库。底部有一个演示应用程序:gngeorgiev.github.io/Modulerr.js 您也可以将所有脚本合二为一,而不依赖于 Modulerr.js 这个问题给了我一生中最美好的时刻之一。通过简单地使用$controllerProvider
,现在我可以拥有self-contained 的客户端代码。即 html + JS 在一个文件中。
可以使用requireJs和ocLazyload来动态加载文件和注入模块。参考文章codeproject.com/Articles/1039826/…
在此处尝试 ocLazyload 示例应用 freakyjolly.com/how-to-lazy-load-modules-controllers-angularjs
【参考方案1】:
你也可以使用 jquery 来解析 $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';
);
【讨论】:
这并不总是有效 - 有时 getScript 在视图呈现后执行。 /*这里很神奇,jquery getScript 会在渲染视图之前将脚本放在我想要的目录中*/ 您好,感谢观看,我制作了一个新版本,现在试试 它也将在任何现代构建系统上失败。【参考方案2】:////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
//控制器声明
angularApp.components.controller('DiscussionController',[function()
]);
【讨论】:
require
只需要 1 个参数。【参考方案3】:
起初我使用了 André Betiolo 的回答。但是,它并不总是有效,因为 ajax 加载是非阻塞的,导致视图有时会在加载脚本之前请求控制器。
作为一种解决方案,我强制该函数在所有脚本成功加载之前不返回。这有点骇人听闻,但确保在完成解析之前加载成功。它还允许加载多个控制器。
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';
);
【讨论】:
我做了一个新版本,现在试试【参考方案4】:你可以有纯 AngularJS 延迟加载。
创建“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;
]);
然后,定义你的配置:
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');
]
);
. . .
在“js/services/vendor.js”上,创建服务为:
var ng = angular.module('app');
ng.lazy.service('vendorService', [ function()
. . .
在“js/controllers/vendor.js”上,创建控制器为:
var ng = angular.module('app');
ng.lazy.controller('vendorController', [ function()
. . .
when 上的“resolve”属性定义了在路由加载之前应该解决哪些承诺。
【讨论】:
我一直在使用你的解决方案,但现在我想在路线更改时取消请求,我使用资源来处理我的请求,请你帮帮我,tnx【参考方案5】:按照您的要求,最好的方法是使用指令并将控制器和模板绑定在一起,以便在适当的时间绑定。目前,除非您像第二个示例中所示声明一个全局函数,否则它不会在正确的时间在 lazy.htm
中发生绑定。
【讨论】:
【参考方案6】:理想情况下 - Angular 会强制您将 HTML 和 JS 分开,因为在新版本中这可能会更频繁地强制执行。
您可能必须使用 requireJS http://solutionoptimist.com/2013/09/30/requirejs-angularjs-dependency-injection/
为了诡计你可以试试
ng-controller-controller="'lazy'"
或
在 HTML 中
ng-controller-controller="myObject.controller"
某处注入
$scope.myObject.controller = $controller('lazy', $scope: $scope)
【讨论】:
【参考方案7】:试试这个 ARI plugin 用于 Angular JS。它可以帮助您按需延迟加载控制器脚本。
【讨论】:
【参考方案8】:你也可以使用指令来加载你的控制器!
这里是一个例子:
https://gist.github.com/raphaelluchini/53d08ed1331e47aa6a87
【讨论】:
【参考方案9】:我正在向您发送示例代码。它对我来说很好。所以请检查:
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
);
])
在 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>
谢谢你
【讨论】:
以上是关于AngularJS:延迟加载控制器和内容的主要内容,如果未能解决你的问题,请参考以下文章