Angular2:在DOM中插入动态组件作为容器的子组件

Ant*_*ine 33 angular

有没有办法在Angular 2中动态插入一个组件作为DOM标记的(而不是兄弟)?

有很多例子可以将动态组件作为给定ViewContainerRef标签的兄弟插入,例如(从RC3开始):

@Component({
  selector: '...',
  template: '<div #placeholder></div>'
})
export class SomeComponent {
  @ViewChild('placeholder', {read: ViewContainerRef}) placeholder;

  constructor(private componentResolver: ComponentResolver) {}

  ngAfterViewInit() {
    this.componentResolver.resolveComponent(MyDynamicComponent).then((factory) => {
        this.componentRef = this.placeholder.createComponent(factory);
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

但这会生成类似于以下内容的DOM:

<div></div>
<my-dynamic-component></my-dynamic-component>
Run Code Online (Sandbox Code Playgroud)

预期结果:

<div>
    <my-dynamic-component></my-dynamic-component>
</div>
Run Code Online (Sandbox Code Playgroud)

使用SomeComponent's ViewContainerRef具有相同的结果,它仍然将生成的组件插入为兄弟,而不是孩子.我可以使用一个解决方案,其中模板为空,动态组件插入模板中(在组件选择器标签内).

当使用像ng2-dragula这样的库从动态组件列表中拖动并从模型更新中受益时,DOM结构非常重要.额外的div位于可拖动元素的列表中,但在模型之外,打破了拖放逻辑.

有人说这是不可能的(参见此评论),但这听起来像是一个非常令人惊讶的限制.

Ant*_*ine 48

TL; DR:更换<div #placeholder></div><div><ng-template #placeholder></ng-template></div>里面的插入div.

这是一个有效的stackblitz示例(Angular 6),以及相关的代码:

@Component({
  selector: 'my-app',
  template: `<div><ng-template #container></ng-template></div>`
})
export class AppComponent implements OnInit {

    @ViewChild('container', {read: ViewContainerRef}) viewContainer: ViewContainerRef;

    constructor(private compiler: Compiler) {}

    ngOnInit() {
      this.createComponentFactory(MyDynamicComponent).then(
        (factory: ComponentFactory<MyDynamicComponent>) => this.viewContainer.createComponent(factory),
        (err: any) => console.error(err));
    }

    private createComponentFactory(/*...*/) {/*...*/}

}
Run Code Online (Sandbox Code Playgroud)

这似乎<ng-container #placeholder></ng-container>也在努力(替换ng-templateng-container).我喜欢这种方法,因为<ng-container>它明确地针对这个用例(一个不添加标签的容器),并且可以在其他情况下使用,例如NgIf不包装在真实标签中.


PS:@GünterZöchbauer在评论中指导我进行了正确的讨论,最后我回答了自己的问题.


编辑[2018-05-30]:更新到stackblitz链接以获得最新的工作示例.


Mag*_*ner 8

我正在寻找相同问题的解决方案,并且批准的答案对我不起作用.但是我发现了如何将动态创建的组件作为子元素附加到当前主机元素的更好和更合理的解决方案.

我们的想法是使用新创建的组件的元素引用,并使用render服务将其附加到当前元素ref.我们可以通过其inject属性获取组件对象.

这是代码:

@Directive({
    selector: '[mydirective]'
})
export class MyDirectiveDirective {
    constructor(
        private cfResolver: ComponentFactoryResolver,
        public vcRef: ViewContainerRef,
        private renderer: Renderer2
    ) {}

    public appendComponent() {
        const factory = 
        this.cfResolver.resolveComponentFactory(MyDynamicComponent);
        const componentRef = this.vcRef.createComponent(factory);
        this.renderer.appendChild(
           this.vcRef.element.nativeElement,
           componentRef.injector.get(MyDynamicComponent).elRef.nativeElement
        );
    }
}
Run Code Online (Sandbox Code Playgroud)


Ben*_*eng 5

我的解决方案与@Bohdan Khodakivskyi 非常相似。但是我试过Renderer2。

  constructor(
    private el: ElementRef,
    private viewContainerRef: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver,
    private render: Renderer2
  ) {}

  ngOnInit() {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
      MyDynamicComponent,
    );
    const componentRef = this.viewContainerRef.createComponent(componentFactory);
    this.render.appendChild(this.el.nativeElement, componentRef.location.nativeElement)
  }
Run Code Online (Sandbox Code Playgroud)