Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2: Creating child components programmatically

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

like image 652
Waog Avatar asked Jun 14 '15 14:06

Waog


4 Answers

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

like image 113
TGH Avatar answered Nov 12 '22 03:11

TGH


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

like image 25
Anshul Avatar answered Nov 12 '22 03:11

Anshul


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.

like image 7
Shivang Gupta Avatar answered Nov 12 '22 03:11

Shivang Gupta


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.

like image 3
xameeramir Avatar answered Nov 12 '22 03:11

xameeramir