angularangular2-injection

Getting parent Component via Injector.get() when token can be multiple values


What I'm trying to do:

Plnkr -> http://plnkr.co/edit/Do4qYfDLtarRQQEBphW3?p=preview

In looking at the angular.io documentation, I've found that "Injector" can be used to obtain the parent Component in the constructor

constructor(private el: ElementRef, private hostComponent:Injector){
    el.nativeElement.draggable = 'true';
}

In doing so, I get Injector Object. From what I can tell I am then supposed to use

this.hostComponent.get(INJECTORTOKEN)

The issues I'm having difficulty grasping is that, the examples provided in Angular, make the assumption that you know the type of Component to provide in the token. ie:

this.hostComponent.get(Image);
this.hostComponent.get(Box);

In the pnkr example, I have two components in my template

<div>
   <h2>Hello {{name}}</h2>
   <my-image></my-image> <!-- Uses the "My Directive" -->
   <my-box></my-box> <!-- Uses the "My Directive" -->
</div>

My questions is, in the "mydirective.ts". how can I leverage the "injector.get()" when I don't know if the parent is an "my-image" or 'my-box' component.

in the sample provided, the directive is triggered "ondrag()". view the Console, for log messages.

Any assistance is gratefully appreciated.

Thank you kindly.


Solution

  • I know several ways to do that:

    1) Find a parent by its class-interface

    You need the provider's class-interface token like:

    export abstract class Parent { }
    

    After that you should write an alias provider on Box and Image component

    box.ts

    providers: [{ provide: Parent, useExisting: forwardRef(() => Box) }]
    

    image.ts

    providers: [{ provide: Parent, useExisting: forwardRef(() => Image) }]
    

    then use it in your directive as shown below

    myDirective.ts

    export class MyDirective {
      constructor(@Optional() private parent: Parent){}
    
      @HostListener('dragstart',['$event']) ondragstart(event){
        switch(this.parent.constructor) {
         case Box:
          console.log('This is Box');
          break;
         case Image:
          console.log('This is Image');
          break;
        }
      }
    }
    

    Here's Plunker

    2) Inject all your parents as Optional token

    myDirective.ts

    export class MyDirective {
      constructor(
        @Optional() private image: Image, 
        @Optional() private box: Box){}
    
        @HostListener('dragstart',['$event']) ondragstart(event){
          if(this.box) {
            console.log('This is Box');
          }
          if(this.image) {
            console.log('This is Image');
        }
      }
    }
    

    Plunker for this case

    3) Use Injector like

    export class MyDirective {
      constructor(private injector: Injector){}
    
      @HostListener('dragstart',['$event']) ondragstart(event){
        const boxComp = this.injector.get(Box, 0);
        const imageComp = this.injector.get(Image, 0);
    
        if(boxComp) {
          console.log('This is Box');
        }
        if(imageComp) {
          console.log('This is Image');
        }
      }
    }
    

    Plunker