It will create a new instance of Angular injector service, which is responsible for dependency injection and controls application life cycle. Consequently, it also creates new instances of the services which are used in the application (including the ones from ng
module: $rootScope
, $compile
, etc).
You may think of it as of a new instance of the application (which is the collection of modules). Angular itself (angular
object) isn't instantiated.
The thing that differs angular.bootstrap
from angular.injector
(the latter just creates a new injector instance) is that it links an injector instance to DOM element by means of $rootElement
service. This way this element and its children are associated with specific injector instance (it can be acquired with angular.element(someElement).injector()
).
It is not possible to bootstrap an app on bootstrapped element (or its children), Angular will protect it from being messed up.
However, Angular is not really fool-proof, it can be done by bootstrapping the apps in reverse:
angular.bootstrap(nestedAppElement, ['nestedApp']);
angular.bootstrap(appElement, ['app']);
Looks like we finally messed the things up. Nothing prevents app
from compiling its own directives in <nested-app-container>
, and it uses its own injector and scope (it is app
root scope here) to compile them, not scope and injector that belong to current DOM element. And the things will become even more messy if nested app will be re-compiled by some parent app directive.
Bypassing Angular fool-proof bypass for double bootstrapping is quite straightforward. Since Angular uses inheritedData to get element's injector in hierarchical manner (when element.data('$injector')
is undefined on bootstrapped element, it is automatically retrieved from parents), it has to be overwritten:
angular.module('app').directive('nestedAppContainer', function () {
return {
compile: function (element) {
element.data('$injector', null);
}
};
});
Now the apps can be safely bootstrapped in any order. But the problem with keeping app
away from <nested-app-container>
is still there. It can be solved either with marking nestedAppContainer
directive as terminal
angular.module('app').directive('nestedAppContainer', function () {
return {
priority: 100000,
terminal: true,
...
};
});
or by putting nested app into shadow DOM
nestedAppContainer = nestedAppContainer.createShadowRoot();
which is specifically intended to isolate DOM parts (natively supported by Chrome and polyfilled in other browsers).
TL;DR: the solution to nested apps in Angular
As it is shown here:
<body>
<div app-dir>
<nested-app-container></nested-app-container>
</div>
</body>
and
var appElement = document.querySelector('body');
var nestedAppContainer = document.querySelector('nested-app-container');
// nestedAppContainer = nestedAppContainer.createShadowRoot();
var nestedAppElement = angular.element('<nested-app>')[0];
angular.element(nestedAppContainer).append(nestedAppElement);
angular.element(nestedAppElement)
.append('<div app-dir>(app-dir)</div>')
.append('<div nested-app-dir>(nested-app-dir)</div>');
angular.element().ready(function () {
angular.bootstrap(appElement, ['app']);
angular.bootstrap(nestedAppElement, ['nestedApp']);
});
angular.module('nestedApp', []).directive('nestedAppDir', function () {
return {
controller: function ($scope) {
$scope.app = 'nested app';
},
compile: function (element) {
element.prepend('nested app is "{{ app }}"');
}
}
});
angular.module('app', []).directive('appDir', function () {
return {
controller: function ($scope) {
$scope.app = 'app';
},
compile: function (element) {
element.prepend('app is "{{ app }}"');
}
}
});
angular.module('app').directive('nestedAppContainer', function () {
return {
priority: 100000,
// 'terminal' is redundant with nestedAppContainer.createShadowRoot()
terminal: true,
compile: function (element) {
// element injector is not 'undefined' anymore,
// so it won't be inherited from parent elements
element.data('$injector', null);
}
};
});
While it may look quite neat, proceed with care. As any other hack that exploits undocumented stuff, this one may conceal adverse side-effects or be broken by new framework release.
There are not so many cases that may be in need of hacks. Most times you have an urge to fight the framework, you're doing it wrong.