Angular2:如何通过编程方式创建子组件

39

问题

如何在父组件内创建子组件,并在视图中显示它们,使用Angular2?如何确保可注入服务被正确地注入到子组件中?

示例

import {Component, View, bootstrap} from 'angular2/angular2';
import {ChildComponent} from './ChildComponent';

@Component({
    selector: 'parent'
})
@View({
  template: `
    <div>
      <h1>the children:</h1>
      <!-- ??? three child views shall be inserted here ??? -->
    </div>`,
  directives: [ChildComponent]
})
class ParentComponent {

        children: ChildComponent[];

        constructor() {
            // when creating the children, their constructors
            // shall still be called with the injectables.
            // E.g. constructor(childName:string, additionalInjectable:SomeInjectable)
            children.push(new ChildComponent("Child A"));
            children.push(new ChildComponent("Child B"));
            children.push(new ChildComponent("Child C"));
            // How to create the components correctly?
        }
}
bootstrap(ParentComponent);

编辑

我在API文档预览中找到了DynamicComponentLoader。但是,当按照示例操作时出现以下错误:元素0处没有动态组件指令


2
谢谢你问这个问题——正是我想要弄清楚的。 - Bryce Johnson
1
请参见https://dev59.com/iFoV5IYBdhLWcg3wY976#36325468。 - Günter Zöchbauer
5个回答

19

我通常不会采取这种方法。相反,我会依靠数据绑定到一个数组上,该数组将渲染更多的子组件,因为对象被添加到支持数组中。本质上是在ng-for中包含的子组件。

我有一个类似于此的示例,它呈现动态子列表。虽然不完全相同,但似乎概念仍然相同:

http://www.syntaxsuccess.com/viewarticle/recursive-treeview-in-angular-2.0


好的例子,还不确定是否回答了我的问题。我对语法有一些后续问题,我在文章下方留下了评论。基本上,我找不到以下内容的文档。在模板中:*[directories]="directories"[checked]="dir.checked"(click)="dir.check()"*。在组件中的 properties: ['directories: directories']... 这些东西叫什么?我可以在哪里阅读相关资料? - Waog
我已经回复了你的评论。 - TGH
4
为了帮助人们理解,“This is generally not the approach I would take”的原因,补充一些相关信息可能会有帮助。 - Fiddles

11

警告:DynamicComponentLoader在RC.4中已被弃用

在Angular 2.0中,DynamicComponentLoaderloadIntoLocation方法用于创建父子关系。通过使用这种方法,您可以动态地在两个组件之间创建关系。

以下是示例代码,其中paper是我的父组件,bulletin是我的子组件。

paper.component.ts

import {Component,DynamicComponentLoader,ElementRef,Inject,OnInit} from 'angular2/core';
import { BulletinComponent } from './bulletin.component';

@Component({
    selector: 'paper',
    templateUrl: 'app/views/paper.html'

    }
})
export class PaperComponent {
    constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {

    }

    ngOnInit(){
        this.dynamicComponentLoader.loadIntoLocation(BulletinComponent, this.elementRef,'child');

    }
}

bulletin.component.ts

import {Component} from 'angular2/core';

@Component({
    selector: 'bulletin',
    template: '<div>Hi!</div>'
    }
})
export class BulletinComponent {}

paper.html

<div>
    <div #child></div>
</div>

这个答案中提到了需要注意的几件事。


你能提供弃用文档的链接吗? - Thomas

7

您应该使用ComponentFactoryResolver和ViewElementRef在运行时添加组件。让我们看一下以下代码。

let factory = this.componentFactoryResolver.resolveComponentFactory(SpreadSheetComponent);
let res = this.viewContainerRef.createComponent(factory);

把以上代码放在你的ngOnInit函数里,并把"SpreadSheetComponent"替换为你的组件名称。
希望这样能够起作用。

3
在 Angular 2/4 应用中,以编程方式向 DOM 中添加组件
我们需要使用 AfterContentInit 接口中的 ngAfterContentInit() 生命周期方法。它在指令内容完全初始化之后调用。
在 parent-component.html 文件中,像这样添加 div:
parent-component.ts 文件如下:


class ParentComponent implements AfterContentInit {

  @ViewChild("container", { read: ViewContainerRef }) divContainer

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

  ngAfterContentInit() {
    let childComponentFactory = this.componentFactoryResolver.resolveComponentFactory(childComponent);
    this.divContainer.createComponent(childComponentFactory);
    let childComponentRef = this.divContainer.createComponent(childComponentFactory);
    childComponentRef.instance.someInputValue = "Assigned value";

  }
}

src\app\app.module.ts 文件中,在 @NgModule() 方法的参数中添加以下条目:

  entryComponents:[
    childComponent
  ],

请注意,我们不是使用“@ViewChild("container") divContainer”方法访问“div#container”。我们需要它的引用而不是“nativeElement”。我们将使用ViewContainerRef来访问它:
@ViewChild("container", {read: ViewContainerRef}) divContainer ViewContainerRef有一个名为createComponent()的方法,需要传递一个组件工厂作为参数。为此,我们需要注入ComponentFactoryResolver。它有一个方法,基本上加载一个组件。

1

正确的方法取决于您要解决的情况。

如果孩子的数量未知,则NgFor是正确的方法。 如果它是固定的,如您所提到的,3个孩子,您可以使用 DynamicComponentLoader 手动加载它们。 手动加载的好处是更好地控制元素并在父级内引用它们(也可以使用模板获得...)

如果您需要使用数据填充孩子,这也可以通过注入完成,父级注入了一个数据对象,在原地填充孩子...

再次强调,有很多选项。 我在我的模态示例中使用了“DynamicComponentLoader”,https://github.com/shlomiassaf/angular2-modal


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接