AJLiu
AJLiu

Reputation: 85

How to dynamically add a component as a child to a directive?

I've been experimenting with Angular 2, and I have been able to dynamically add a component to the child of another by getting the @ViewChild of an existing child then using ComponentResolver to add my dynamic component.

However, what should you do if you do not have control over the template (i.e. you're dynamically adding a component to the child of a directive)? I don't know what elements would be in the container with my directive, so how would I get a ViewContainerRef for something like that?

EDIT: I seem to also have issues getting the @ViewChild of a directive. Here's a plunker of what I have going on in my app..

Hello.component.ts

import { Component } from '@angular/core'

@Component({
  selector: 'hello',
  template: '<h1>Hi!</h1>',
  styles: ['h1{background-color: green}']
})
export class HelloComponent {}

Test.directive.ts

import { Directive, ViewChild, ViewContainerRef, ComponentResolver, OnInit } from '@angular/core';
import { HelloComponent } from './hello.component'

@Directive({
  selector: '[testDir]'
})
export class TestDirective implements OnInit {
  @ViewChild('div', {read: ViewContainerRef}) div;

  constructor(private viewContainerRef: ViewContainerRef, private componentResolver: ComponentResolver) {}

  ngOnInit() {
    this.componentResolver.resolveComponent(HelloComponent).then((factory) => {
      this.div.createComponent(factory);
      this.viewContainerRef.createComponent(factory);
    });
  }
}

app.component.ts

import { Component } from '@angular/core';
import { TestDirective } from './app.directive';
import { HelloComponent } from './hello.component'

@Component({
  selector: 'my-app',
  template: `
    <div testDir>
      <div #div></div>
    </div>
  `,
  directives: [TestDirective, HelloComponent]
})
export class AppComponent { 
}

Upvotes: 2

Views: 3908

Answers (1)

nircraft
nircraft

Reputation: 8478

You can add a component to the child of your directive using the Renderer2 service.

This example uses element reference of newly created component and appends it to current element ref by using render service.

Below is one directive that does so on click of it, you can perform the same action onInit too:

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


  @HostListener('click', ['$event'])
  onClick() {
    const factory =
      this.cfResolver.resolveComponentFactory(HelloComponent);
    const componentRef = this.vcRef.createComponent(factory);
    this.renderer.appendChild(
      this.vcRef.element.nativeElement,
      componentRef.injector.get(HelloComponent).elementRef.nativeElement
    );
  }
}

You can also set any of the Input properties of the Dynamic component before attaching it to current host element.

Upvotes: 2

Related Questions