动态加载AngularJS控制器

Jus*_*nen 64 javascript angularjs

我有一个现有的页面,我需要删除一个带有可以动态加载的控制器的角度应用程序.

这是一个片段,它根据API和我发现的一些相关问题实现了我应该如何完成的最佳猜测:

// Make module Foo
angular.module('Foo', []);
// Bootstrap Foo
var injector = angular.bootstrap($('body'), ['Foo']);
// Make controller Ctrl in module Foo
angular.module('Foo').controller('Ctrl', function() { });
// Load an element that uses controller Ctrl
var ctrl = $('<div ng-controller="Ctrl">').appendTo('body');
// compile the new element
injector.invoke(function($compile, $rootScope) {
    // the linker here throws the exception
    $compile(ctrl)($rootScope);
});
Run Code Online (Sandbox Code Playgroud)

JSFiddle.请注意,这是实际事件链的简化,上面的行之间有各种异步调用和用户输入.

当我尝试运行上面的代码时,$ compile返回的链接器抛出:Argument 'Ctrl' is not a function, got undefined.如果我正确理解了引导程序,它返回的注入器应该知道Foo模块,对吧?

相反,如果我使用一个新的注入器angular.injector(['ng', 'Foo']),它似乎工作,但它创建一个新的$rootScope,不再与Foo模块被引导的元素相同的范围.

我使用正确的功能来做这件事还是我错过了什么?我知道这不是Angular方式,但是我需要添加使用Angular的新组件到没有的旧页面,而且我不知道在引导模块时可能需要的所有组件.

更新:

我已经更新了小提琴,表明我需要能够在不确定的时间点向页面添加多个控制器.

Jus*_*nen 71

我找到了一个可能的解决方案,在引导之前我不需要知道控制器:

// Make module Foo and store $controllerProvider in a global
var controllerProvider = null;
angular.module('Foo', [], function($controllerProvider) {
    controllerProvider = $controllerProvider;
});
// Bootstrap Foo
angular.bootstrap($('body'), ['Foo']);

// .. time passes ..

// Load javascript file with Ctrl controller
angular.module('Foo').controller('Ctrl', function($scope, $rootScope) {
    $scope.msg = "It works! rootScope is " + $rootScope.$id +
        ", should be " + $('body').scope().$id;
});
// Load html file with content that uses Ctrl controller
$('<div id="ctrl" ng-controller="Ctrl" ng-bind="msg">').appendTo('body');

// Register Ctrl controller manually
// If you can reference the controller function directly, just run:
// $controllerProvider.register(controllerName, controllerFunction);
// Note: I haven't found a way to get $controllerProvider at this stage
//    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) {
            controllerProvider.register(controllerName, call[2][1]);
        }
    }
}
registerController("Foo", "Ctrl");
// compile the new element
$('body').injector().invoke(function($compile, $rootScope) {
    $compile($('#ctrl'))($rootScope);
    $rootScope.$apply();
});
Run Code Online (Sandbox Code Playgroud)

小提琴.唯一的问题是你需要存储$controllerProvider并在不应该使用它的地方使用它(在引导程序之后).在注册之前,似乎没有一种简单的方法来获取用于定义控制器的函数,因此我需要遍历模块_invokeQueue,这是未记录的.

更新:注册指令和服务,而不是$controllerProvider.register简单地分别使用$compileProvider.directive$provide.factory.同样,您需要在初始模块配置中保存对这些的引用.

UDPATE 2: 这是一个小提琴,可以自动注册所有加载的控制器/指令/服务,而无需单独指定它们.


Mar*_*cok 17

bootstrap()会为你调用AngularJS编译器,就像ng-app一样.

// Make module Foo
angular.module('Foo', []);
// Make controller Ctrl in module Foo
angular.module('Foo').controller('Ctrl', function($scope) { 
    $scope.name = 'DeathCarrot' });
// Load an element that uses controller Ctrl
$('<div ng-controller="Ctrl">{{name}}</div>').appendTo('body');
// Bootstrap with Foo
angular.bootstrap($('body'), ['Foo']);
Run Code Online (Sandbox Code Playgroud)

小提琴.


Mar*_*tis 7

我建议看看ocLazyLoad库,它在运行时注册模块(或现有模块上的控制器,服务等),并使用requireJs或其他此类库加载它们.