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.
The above code is only an example.
This way you don't need to put
ng-controller="someController"
anywhere on a page — you only declare<body ng-app="mainApp">
Same structure can be used for each module or modules inside modules
why not use config and ui-router?
it is loaded at runtime and you have no need to show your controllers in html code
for example something like the following
This is what I did, 2 parts really, using ng-controller with its scope defined function and then $controller service to create the dynamic controller :-
First, the HTML - we need a Static Controller which will instantiate a dynamic controller ..
The static controller 'staticCtrl' defines a scope member called 'dynamicCtrl' which is called to create the dynamic controller. ng-controller will take either a predefined controller by name or looks at current scope for function of same name ..
We use eval() to take a string (our dynamic code which can come from anywhere) and then the $controller service which will take either a predefined controller name (normal case) or a function constructor followed by constructor parameters (we pass in a new scope) - Angular will inject (like any controller) into the function, we are requesting just $scope and $rootScope above.
I also needed to add multiple views and bind them to controllers at runtime from a javascript function outside the angularJs context, so here's what I came up with :
now calling setCnt() function will inject and compile the html, and it will be linked to the 2nd controller:
here's an example to test this : http://refork.com/x4bc
hope this helps.
I've found a possible solution where I don't need to know about the controller before bootstrapping:
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.
bootstrap() will call the AngularJS compiler for you, just like ng-app.
Fiddle.