将在“n"上调用 angular.bootstrap多个DOM节点,实例化“n个"AngularJS应用程序? [英] Will invoking angular.bootstrap on "n" multiple DOM nodes, instantiate "n "AngularJS applications?
问题描述
说调用 angular.bootstrap
会创建一个新的 AngularJS 实例,以及一个单独的摘要循环、根作用域、注入器等是否正确?
如果是这样,是否可以嵌套此类应用程序(即在由另一个 AngularJS 应用程序管理的 DOM 内的 DOM 节点上调用引导程序)?(我认为没有!)
它将创建一个新的 Angular 实例 injector 服务,负责依赖注入并控制应用生命周期.因此,它还创建了应用程序中使用的服务的新实例(包括来自 ng
模块的那些:$rootScope
, $compile
等).
您可以将其视为应用程序的一个新实例(即模块的集合).Angular 本身(angular
对象)没有被实例化.
angular.bootstrap
与 angular.injector
(后者只是创建一个新的注入器实例)的不同之处在于它通过$rootElement<将注入器实例
链接到DOM元素/代码>服务.这样这个元素及其子元素就与特定的注入器实例相关联(它可以通过 angular.element(someElement).injector()
获取).
不可能在引导元素(或其子元素)上引导应用程序,Angular 将保护它不被弄乱.
然而,Angular 并不是真的万无一失,它可以通过反向引导应用程序 来完成:
angular.bootstrap(nestedAppElement, ['nestedApp']);angular.bootstrap(appElement, ['app']);
看起来我们终于把事情搞砸了.没有什么可以阻止 app
在
中编译自己的指令,并且它使用自己的注入器和作用域(它是 app
> 这里的根作用域)来编译它们,而不是属于当前 DOM 元素的作用域和注入器.如果嵌套应用程序将被某些父应用程序指令重新编译,事情会变得更加混乱.
绕过用于双重引导的 Angular 万无一失的绕过非常简单.由于 Angular 使用 inheritedData 以分层方式获取元素的注入器(当element.data('$injector')
在引导元素上未定义,它会自动从父元素中检索),它必须被覆盖:
angular.module('app').directive('nestedAppContainer', function () {返回 {编译:函数(元素){element.data('$injector', null);}};});
现在应用可以以任何顺序安全引导.但是让 app
远离
的问题仍然存在.可以通过将 nestedAppContainer
指令标记为终端
angular.module('app').directive('nestedAppContainer', function () {返回 {优先级:100000,终端:真的,...};});
或将嵌套应用程序放入shadow DOM>
nestedAppContainer = nestedAppContainer.createShadowRoot();
专门用于隔离 DOM 部分(Chrome 原生支持,其他浏览器使用 polyfill).
<小时>TL;DR:Angular 中嵌套应用的解决方案
因为它是 此处显示:
<div app-dir><nested-app-container></nested-app-container>
和
var appElement = document.querySelector('body');var nestedAppContainer = document.querySelector('nested-app-container');//nestedAppContainer = nestedAppContainer.createShadowRoot();varnestedAppElement = angular.element('<nested-app>')[0];angular.element(nestedAppContainer).append(nestedAppElement);angular.element(nestedAppElement).append('<div app-dir>(app-dir)</div>').append('<divnested-app-dir>(nested-app-dir)</div>');angular.element().ready(function () {angular.bootstrap(appElement, ['app']);angular.bootstrap(nestedAppElement, ['nestedApp']);});angular.module('nestedApp', []).directive('nestedAppDir', function () {返回 {控制器:函数($scope){$scope.app = '嵌套应用';},编译:函数(元素){element.prepend('嵌套的应用程序是{{ app }}"');}}});angular.module('app', []).directive('appDir', function () {返回 {控制器:函数($scope){$scope.app = 'app';},编译:函数(元素){element.prepend('app is "{{ app }}"');}}});angular.module('app').directive('nestedAppContainer', function () {返回 {优先级:100000,//'terminal' 是多余的 NestedAppContainer.createShadowRoot()终端:真的,编译:函数(元素){//元素注入器不再是未定义",//所以它不会从父元素继承element.data('$injector', null);}};});
虽然它看起来很整洁,但请小心操作.与利用未记录内容的任何其他黑客一样,此黑客可能会隐藏不利的副作用或被新框架发布所破坏.
可能需要 hack 的案例并不多.大多数情况下,您有一种与框架抗争的冲动,但您做错了.
Is it correct to say that invoking angular.bootstrap
will create a new instance of AngularJS, together with a separate digest cycle, root scope, injector and so on?
If so, is it possible to nest such applications (i.e. invoke bootstrap on DOM nodes inside the DOM managed by another AngularJS app)? (I presume no!)
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.
这篇关于将在“n"上调用 angular.bootstrap多个DOM节点,实例化“n个"AngularJS应用程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!