2017-06-13 83 views
1

我有经由到HTML服务注入任何成分的工作代码:Angular - 服务注入动态组件?

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 。:

/*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> 
    `, 
}) 

结果(点击一个按钮调用该服务的方法时):

enter image description here

我已经知道contentCmpFactorywindowCmpFactory是(行#8,9

但是我不认为以后会发生什么。关于第#11,第12行 - 文档说“创建了一个新组件”。

问题:

1 - 线#12:什么是[[contentCmpt.location.nativeElement]]办? (的文档说,它的类型是projectableNodes?: any[][] - 这是什么意思?)

2 - 线#14:什么是[[windowCmpt.location.nativeElement]]办?

3行#16,#17:如果我已经做了appendChild,那么为什么需要它们? (docs说:附加一个视图,以便它会被检查脏。 - 所以?)。

PLUNKER

回答

4

数目:

1)角取ComponentFactory并用给定的元件喷射器创建组件实例,并与可投影节点

windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]); 

1.1元喷射器的阵列将会使用角度为时

const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR); 

这里还简单地说明了应用程序的依赖性解析算法,没有延迟加载。使用延迟加载它看起来更复杂。

enter image description here

欲了解更多详情,请参阅设计文档element injector vs module injector

1.2可放映节点是节点的元素,这是“预测”(transcluded)中,我们已经在我们的组件的模板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组件的主机元素获取参考(contentCmpt.location)。这是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吸气存储参考host元素

export abstract class ComponentRef<C> { 
    /** 
    * Location of the Host Element of this Component Instance. 
    */ 
    abstract get location(): ElementRef; 

,然后在document.body标签的最后一个子inseting它。所以我们在页面上看到它。

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)。角度开放的API用于这样的目的,我们可以很容易地将视图添加到根viewshttps://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L428和之后,我们的组件将在Application.tickhttps://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L558

+0

♡♡一如既往....谢谢。多好的答案。 –

+0

@yuruzi,但如果我有两个(名为'ng-content select = ...')ng内容,我该如何映射每个部分('header'到第一个ng-content,'body'到第二个ng-content)? https://plnkr.co/edit/ozjVRwM3VMzNVErbH92O?p=preview –

+0

'[contentCmpt.location.nativeElement.children]'https://plnkr.co/edit/UnahRZEVa0diSxnKH3D8?p=preview – yurzui