我有一个可以通过服务将任何组件注入HTML的工作代码:
ModalWindow.ts:
@Component({
selector: 'modal-window'
template: `
<div class="modal-dialog" role="document">
<div class="modal-content"><ng-content></ng-content></div>
</div>
`
})
export class ModalWindow {
}
Modalcontent.ts:
@Component({
selector: 'modal-content'
template: `
I'm beeing opened as modal!
`
})
export class ModalContent {
}
ModalService.ts:
/*1*/ @Injectable()
/*2*/ export class ModalService {
/*3*/
/*4*/ constructor(private _appRef: ApplicationRef, private _cfr: ComponentFactoryResolver, private _injector: Injector) {
/*5*/ }
/*6*/
/*7*/ open(content: any) {
/*8*/ const contentCmpFactory = this._cfr.resolveComponentFactory(content);
/*9*/ const windowCmpFactory = this._cfr.resolveComponentFactory(ModalWindow);
/*10*/
/*11*/ const contentCmpt = contentCmpFactory.create(this._injector);
/*12*/ const windowCmpt = windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);
/*13*/
/*14*/ document.querySelector('body').appendChild(windowCmpt.location.nativeElement);
/*15*/
/*16*/ this._appRef.attachView(contentCmpt.hostView);
/*17*/ this._appRef.attachView(windowCmpt.hostView);
/*18*/ }
/*19*/ }
App.ts:
@Component({
selector: 'my-app',
template: `
<button (click)="open()">Open modal window</button>
`,
})
结果(单击调用此服务方法的按钮时):
我已经知道contentCmpFactory
和windowCmpFactory
是什么(第8,9行)
但我不知道以后会发生什么事情。关于第11行,第12行 - 文档说&#34;创建一个新组件&#34;。
问题:
1 - 第12行:[[contentCmpt.location.nativeElement]]
做什么? (文档说它的类型是projectableNodes?: any[][]
- 它们是什么意思??)
2 - 第14行:[[windowCmpt.location.nativeElement]]
做什么?
3 - 第16行,#17:如果我已经appendChild
,我需要什么以及为什么需要它们? ( docs说:附加一个视图,以便检查它。 - 所以?)。
答案 0 :(得分:5)
<强>数目:强>
1) Angular接受ComponentFactory
并使用给定的元素注入器和可投影节点数组创建组件实例
windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);
1.1 当角度resolve dependency
时,将使用元素注入器const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR);
这里也是没有延迟加载的应用程序的依赖性解析算法的简单说明。随着延迟加载,它看起来会更复杂。
有关详细信息,请参阅设计文档element injector vs module injector
1.2 可投影节点是节点元素,它们在我们组件模板中的ng-content
中“投影”(被转换)。
为了投射某些东西,我们的组件模板必须包含ng-content
节点。
@Component({
selector: 'modal-window',
template: `
<div class="modal-dialog">
<div class="modal-content">
<ng-content></ng-content> // <== place for projection
</div>
</div>
`
})
export class ModalWindow {
我们可以在父组件模板中使用上面的组件,如下所示:
<modal-window>
<modal-content></modal-content>
<div>Some other content</div>
</modal-window>
所以我们的最终结果如下:
<modal-window>
<div class="modal-dialog">
<div class="modal-content">
<modal-content></modal-content> // our projectable nodes
<div>Some other content</div> // replaced ng-content
</div>
</div>
</modal-window>
所以当我们传递可投影节点来创建方法
时windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);
我们做同样的事情。
我们将引用(contentCmpt.location
)引用到已创建的早期contentCmpt
组件的主机元素。这是modal-content
元素。然后,角度将完成所有魔法,将其投射到ng-content
位置。
在上面的示例中,我添加了一个div
<modal-window>
<modal-content></modal-content>
<div>Some other content</div> <== here
</modal-window>
所以真正的代码应该是这样的:
let div = document.createElement('div');
div.textContent = 'Some other content';
windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement, div]]);
总结Why is projectableNodes an any[][]?
2)在下一行
document.querySelector('body').appendChild(windowCmpt.location.nativeElement);
我们正在获取对内存modal-window
元素中创建的引用。 ComponentRef
允许我们这样做,因为它存储了对location
getter
export abstract class ComponentRef<C> {
/**
* Location of the Host Element of this Component Instance.
*/
abstract get location(): ElementRef;
然后将document.body
标记中的内容作为最后一个子项进行分类。所以我们在页面上看到它。
3)假设我们的ModalContent
不仅包含静态内容,还会执行一些互动操作。
@Component({
selector: 'modal-content',
template: `
I'm beeing opened as modal! {{ counter }}
<button (click)="counter = counter + 1">Increment</button>
`
})
export class ModalContent {
counter = 1;
}
如果我们删除
this._appRef.attachView(contentCmpt.hostView);
然后我们的视图将不会在更改检测周期中更新,因为我们通过ComponentFactory.create
创建了视图,并且我们的视图不是更改检测树中任何项目的一部分(与通过ViewContainerRef.createComponent
创建不同)。 Angular为此目的打开了API,我们可以轻松地向根views
https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L428添加视图,之后我们的组件将在Application.tick
https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L558期间更新