61

How to dynamically add (inject) a directive into host?

I have a myTooltip directive and I would like to add mdTooltip directive to it's host. I have tried setAttribute() of ElementRef.nativeElement, but it doesn't create the mdTooltip directive.

mytooltip.directive.ts:

@Directive({
  selector: '[my-tooltip]',
  host: {
    '(mouseenter)': 'show()',
    '(mouseleave)': 'hide()',
  }
})
export class myTooltip {
  @Input('my-tooltip') message;

  constructor() { }

  show() {
    /* TODO: How to add md-tooltip directive to elementref (host)? */
  }

  hide() {
    /* TODO: remove md-tooltip directive from elementref (host) */
  }
}

By host I mean the element that has myTooltip directive:

<span my-tooltip="tooltip hint">Click here</span>

The result wouldn't change above html but on mouseenter it would have md-tooltip directive in span.

BTW, the reason I am using a wrapper and not directly md-tooltip is that I want to later modify the showing delay, hiding delay and customize material tooltip's behaviour in other means as well.

Edit Apparently adding directives dynamically is not currently supported :( I think this question should still be here in case it material team updates that

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
RichieRock
  • 1,138
  • 1
  • 8
  • 13
  • 1
    Have you tried `constructor(private tt:myTooltip) { console.log(tt); }`? Not sure if this works. What is "host" actually? `` doesn't look like a component. – Günter Zöchbauer Dec 23 '16 at 09:02
  • 2
    You're talking about "injecting" when you request a provider in constructor, but in this case I don't mean that. What I mean by injecting here is to have a possibility to insert a directive into an already rendered dom element, a span, in this case. You would do this in html like this `Click here`, but now I'm looking for a programmatic way of doing it. – RichieRock Dec 23 '16 at 09:10
  • 1
    That's not named or related to injecting. Adding directives dynamically is currently not supported, only components can be added dynamically. – Günter Zöchbauer Dec 23 '16 at 09:29
  • 1
    Yes, sorry for misleading naming. I'll change the title to something more describing. I also read something about adding components dynamically, but that didn't apply for adding directives dynamically, thanks for the info. – RichieRock Dec 23 '16 at 09:32

2 Answers2

54

That is a feature we are asking for in angular...read this: https://github.com/angular/angular/issues/8785

A quick and dirty way to do it is to use:

I have a directive named myHilite (to highlight text), I also have a component named MainComponent.ts. In MainComponent.ts I added this line of code...

export class MainComponent {
    @HostBinding('attr.myHilite') myHiliteDirective = new myHilite();
} 

If your directive requires parameters...

export class MainComponent {
    @HostBinding('attr.myHilite') myHiliteDirective = new myHilite(this.elementRef);
}

Your directive may need to execute code in one of its life cycle hooks, manually call the directive's lifecycle hook method in the parent component's lifecycle hook method like this...

export class MainComponent {
    //...code...

    ngOnInit(){
        this.myHiliteDirective.ngOnInit();
    }
}
coderdark
  • 1,481
  • 14
  • 30
  • 1
    @btinoco one more thing I want to clear is `@HostBinding(attr.myDirective)` is in child component and `ngOnInit(){ this.myHilite.ngOnInit(); }` is in parent's component. am I right? – WasiF Apr 16 '18 at 03:10
  • 4
    @btinoco can you please change `myHilite` to `myHiliteDirective` or `myDirective` so that other readers should not be confused about this. – WasiF Apr 16 '18 at 03:12
  • 1
    I edited the answer according to your comment, WasiF, but someone reverted it back to the original solution. Sorry. – Lars Gyrup Brink Nielsen Oct 02 '18 at 07:11
  • 3
    This worked for me nicely. However, is there a way to remove the directive afterwards? Basically, I want to be able to dynamically apply or remove the directive. – AsGoodAsItGets Oct 22 '18 at 08:07
  • If I understand correctly, this wouldn't work with directives that have hostlisteners as those would not be attached to the nativeElement, right? – Felipe Centeno Dec 19 '18 at 23:52
  • 1
    @FelipeCenteno sorry I have not try that – coderdark Dec 20 '18 at 20:24
  • @FelipeCenteno Seems like it wouldn't, but if lifecycle hooks are manual then initializing listeners on the directive can be done via a manual invoked method on the directve as well to initialize the listeners – kamcknig Mar 07 '21 at 14:47
3

Looks like this is coming in Angular 15.

See: https://www.angularjswiki.com/angular/directive-composition-api-in-angular-15/

Dan King
  • 3,412
  • 5
  • 24
  • 23