Add a component dynamically to a child element using a directive
Asked Answered
K

2

11

Trying to place a component dynamically to a child element, using a directive.

The component (as template):

@Component({
  selector: 'ps-tooltip',
  template: `
    <div class="ps-tooltip">
      <div class="ps-tooltip-content">
        <span>{{content}}</span>
      </div>
    </div>
  `
})
export class TooltipComponent {

  @Input()
  content: string;

}

the directive:

import { TooltipComponent } from './tooltip.component';

@Directive({
  selector: '[ps-tooltip]',
})
export class TooltipDirective implements AfterViewInit {

  @Input('ps-tooltip') content: string;

  private tooltip: ComponentRef<TooltipComponent>;

  constructor(
      private viewContainerRef: ViewContainerRef,
      private resolver: ComponentFactoryResolver,
      private elRef: ElementRef,
      private renderer: Renderer
  ) { }

  ngAfterViewInit() {
    // add trigger class to el
    this.renderer.setElementClass(this.elRef.nativeElement, 'ps-tooltip-trigger', true); // ok

    // factory comp resolver
    let factory = this.resolver.resolveComponentFactory(TooltipComponent);

    // create component
    this.tooltip = this.viewContainerRef.createComponent(factory);
    console.log(this.tooltip);

    // set content of the component
    this.tooltip.instance.content = this.content as string;
  }
}

The problem is that this is creating a sibling and I want a child (see bellow)

result:

<a class="ps-btn ps-tooltip-trigger" ng-reflect-content="the tooltip">
  <span>Button</span>
</a>
<ps-tooltip>...</ps-tooltip>

wanted result:

<a class="ps-btn ps-tooltip-trigger" ng-reflect-content="the tooltip">
  <span>Button</span>
  <ps-tooltip>...</ps-tooltip>
</a>

Thanks in advance for your help!

Kelly answered 4/3, 2017 at 16:12 Comment(2)
There is no way to do that. You would need a ViewContainerRef of a child where the added component can be a sibling of. That's how ViewContainerRef.createComponent() works.Masry
Thanks for your answer. Can I ask what you suggest to get the desired result?!Kelly
S
15

Even dynamic component is inserted as sibling element you can still move element to desired place by using:

this.elRef.nativeElement.appendChild(this.tooltip.location.nativeElement);

Plunker Example

Stafani answered 4/3, 2017 at 17:43 Comment(1)
You absolute hero!Lynlyncean
J
3

A better approach would be to have a nested ng-template with template reference variable on it such that the component is added as a sibling to ng-template but is now child to ng-template's parent.

Your template should be

<div class="ps-tooltip">
  <div class="ps-tooltip-content">
    <span>{{content}}</span>
    <ng-template #addHere></ng-template>
  </div>
</div>

And in your component

@ViewChild('addHere') addHere: ViewContainerRef;

ngAfterViewInit() {
    ...

    this.tooltip = addHere.createComponent(factory)

    ...
}
Jacques answered 7/6, 2020 at 10:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.