AngularJS原生并不支持动态加载Controller的方法,但是却提供注册Controller的方法。接下来就来看下如何实现动态加载Controller。

我们把实现动态加载Controller方法封装到一个通用的模块里面,并命名这个模块为ngCommon

1
2
3
4
(function (angular) {'use strict';
var CommonApp = angular.module('ngCommon');
...
})(angular);

接下来我们实现一个动态加载js的方法$require

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* 记录已加载的js */
var loaded = {};
/* 检测是否加载 */
var checkLoaded = function (url) {
return !url || !angular.isString(url) || loaded[url];
};

CommonApp.factory('$require', ['$document', '$q', '$rootScope', function ($document, $q, $rootScope) {
return function (url) {
var script = null;
var onload = null;
var doc = $document[0];
var body = doc.body;
var deferred = $q.defer();
if (checkLoaded(url)) {
deferred.resolve();
} else {
script = doc.createElement('script');
onload = function (info) {
if (info === 1) {
deferred.reject();
} else {
loaded[url] = 1;
/* AngularJS < 1.2.x 请使用$timeout */
$rootScope.$evalAsync(function () {
deferred.resolve();
});
}
script.onload = script.onerror = null;
body.removeChild(script);
script = null;
};
script.onload = onload;
script.onerror = function () {
onload(1);
};
script.async = true;
script.src = url;
body.appendChild(script);
}
return deferred.promise;
};
}]);

然后重点来了,通过$routeProvider routeresolve功能来实现动态加载Controller。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
CommonApp.provider('$routeResolver', function () {
this.$get = function () {
return this;
};
this.route = function (routeCnf) {
var controller = routeCnf.controller;
var controllerUrl = routeCnf.controllerUrl;
if (controllerUrl) {
routeCnf.reloadOnSearch = routeCnf.reloadOnSearch || false;
routeCnf.resolve = {
load: ['$route', '$require', 'ControllerChecker',
function ($route, $require, ControllerChecker) {
var controllerName = angular.isFunction(controller) ? controller($route.current.params) : controller;
var url = angular.isFunction(controllerUrl) ? controllerUrl($route.current.params) : controllerUrl;
if (checkLoaded(url) || (controllerName && ControllerChecker.exists(controllerName))) {
loaded[url] = true;
return;
}
return $require(url);
}]
};
}
return routeCnf;
};
})

看上面的代码中还注入了一个叫ControllerChecker的,这个是用来检测当前Controller是否已经注册了,如果未注册,那么我们就加载相关js注册新的Controller。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CommonApp.service('ControllerChecker', ['$controller', function ($controller) {
return {
exists: function (controllerName) {
if (angular.isFunction(window[controllerName])) {
return true;
}
try {
$controller(controllerName, {}, true);
return true;
} catch (e) {
return false;
}
}
};
}]);

最后我们来添加一个注动态册的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CommonApp.setupRegister = function (module) {
module.config([
'$controllerProvider',
'$compileProvider',
'$filterProvider',
'$provide',
function ($controllerProvider, $compileProvider, $filterProvider, $provide) {
module.register = {
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service,
value: $provide.value,
constant: $provide.constant
};
}
]);
};

到此已经基本完成了,如何使用呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var DemoApp = angular.module('DemoApp',['ngRoute','ngCommon']);
/* 调用动态注册方法,为当前模块添加动态注册方法 */
angular.module('ngCommon').setupRegister(DemoApp);
DemoApp.config(['$routeProvider', '$routeResolverProvider', function ($routeProvider, $routeResolverProvider) {
var route = $routeResolverProvider.route;
$routeProvider.when('/index', route({
templateUrl: './view/index.html'),
controller: 'IndexController', /* 在此申明了controller就不需要再html里面申明ng-controller了 */
controllerUrl: './controller/index.js')
}))
.otherwise('/index');

/* ./controller/index.js */
DemoApp.register.controller('IndexController', ['$scope', '$require', function($scope, $require) {
...
/* 动态加载某个js文件 */
$require(url).then(function () {
...
});
}]);