Angular2: Creating child components programmatical

2020-02-08 04:11发布

问题:

Question

How to create child components inside a parent component and display them in the view afterwards using Angular2? How to make sure the injectables are injected correctly into the child components?

Example

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);

Edit

I found the DynamicComponentLoader in the API docs preview. But I get the following error when following the example: There is no dynamic component directive at element 0

回答1:

This is generally not the approach I would take. Instead I would rely on databinding against an array that will render out more child components as objects are added to the backing array. Essentially child components wrapped in an ng-for

I have an example here that is similar in that it renders a dynamic list of children. Not 100% the same, but seems like the concept is still the same:

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



回答2:

Warning: DynamicComponentLoader has been deprecated in RC.4

In Angular 2.0, loadIntoLocation method of DynamicComponentLoader serve this purpose of creating parent-child relationship. By using this approach you can dynamically create relationship between two components.

Here is the sample code in which paper is my parent and bulletin is my child component.

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>

Few things you needs to be take care of are mentioned in this answer



回答3:

You should use ComponentFactoryResolver and ViewElementRef to add component at runtime.Let's have a look at below code.

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

Put the above code inside your ngOnInit function and replace "SpreadSheetComponent" by your component name.

Hope this will work.



回答4:

Programmatically add components to DOM in Angular 2/4 app

We need to use ngAfterContentInit() lifecycle method from AfterContentInit. It is called after the directive content has been fully initialized.

In the parent-component.html, add the a div like this:

<div #container> </div>

The parent-component.ts file looks like this:



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";

  }
}

Inside src\app\app.module.ts, add the following entry to the @NgModule() method parameters:


  entryComponents:[
    childComponent
  ],

Notice that we're not accessing the div#container using the @ViewChild("container") divContainer approach. We need it's reference instead of the nativeElement. We will access it as ViewContainerRef:

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

The ViewContainerRef has a method called createComponent() which requires a component factory to be passed as a parameter. For the same, we need to inject a ComponentFactoryResolver. It has a method which basically loads a component.



回答5:

The right approach depends on the situation you're trying to solve.

If the number of children is unknown then NgFor is the right approach. If it is fixed, as you mentioned, 3 children, you can use the DynamicComponentLoader to load them manually. The benefits of manual loading is better control over the elements and a reference to them within the Parent (which can also be gained using templating...)

If you need to populate the children with data, this can also be done via injection, the Parent is injected with a data object populating the children in place...

Again, a lot of options. I have used 'DynamicComponentLoader' in my modal example, https://github.com/shlomiassaf/angular2-modal