Issue
I have an existing page into which I need to drop an angular app with controllers that can be loaded dynamically.
Here's a snippet which implements my best guess as to how it should be done based on the API and some related questions I've found:
// 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);
});
JSFiddle. Note that this is a simplification of the actual chain of events, there are various async calls and user inputs between the lines above.
When I try to run the above code, the linker which is returned by $compile throws: Argument 'Ctrl' is not a function, got undefined
. If I understood bootstrap correctly, the injector it returns should know about the Foo
module, right?
If instead I make a new injector using angular.injector(['ng', 'Foo'])
, it seems to work but it creates a new $rootScope
which is no longer the same scope as the element where the Foo
module was bootstrapped.
Am I using the right functionality to do this or is there something I've missed? I know this isn't doing it the Angular way, but I need to add new components that use Angular to old pages that don't, and I don't know all the components that might be needed when I bootstrap the module.
UPDATE:
I've updated the fiddle to show that I need to be able to add multiple controllers to the page at undetermined points in time.
Solution
I've found a possible solution where I don't need to know about the controller before bootstrapping:
// 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();
});
Fiddle. Only problem is that you need to store the $controllerProvider
and use it in a place where it really shouldn't be used (after the bootstrap). Also there doesn't seem to be an easy way to get at a function used to define a controller until it is registered, so I need to loop through the module's _invokeQueue
, which is undocumented.
UPDATE: To register directives and services, instead of $controllerProvider.register
simply use $compileProvider.directive
and $provide.factory
respectively. Again, you'll need to save references to these in your initial module config.
UDPATE 2: Here's a fiddle which automatically registers all controllers/directives/services loaded without having to specify them individually.
Answered By - Jussi Kosunen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.