Home > Enterprise >  Angular - Get the selector name of the directive
Angular - Get the selector name of the directive

Time:03-05

I created a directive with multiple selectors, so I can access the same directive with different selectors. What I'm trying to achieve is to get the selector name that called the directive in my directive.

The following code will explain this even better:

So this is my multi-selector directive:

import { Directive, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';

const inputs = [
  'someSelector.A', 'someSelector.B'
];

const selector = `
  [someSelector.A], [someSelector.B]
`;

@Directive({ selector, inputs })
export class MyDirective implements OnInit {

  constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { }

  ngOnInit() {
    // I need to know here what is the current selector:
    // Is it: "someSelector.A" or "someSelector.B"?
  }
}

And this is how I would like to call the directive in the HTML:

<element *someSelector.A></element>

<!-- Or: -->

<element *someSelector.B></element>

CodePudding user response:

Okay, so the way that flex is doing it, is that they are passing the inputs to the decorator, then the variables are generated on the directive. If the directive is not passed to the html element it is undefined on the directive inputs. It looks like this:

export interface DirectiveAB {
  'someSelector.A': string;
  'someSelector.B': number;
}
type Selectors =  'someSelector.A' | 'someSelector.B';

const inputs = ['someSelector.A', 'someSelector.B'];

@Directive({ selector: '[someSelector.A], [someSelector.B]', inputs })
export class DirectiveAB implements OnInit {

  ngOnInit() {
    console.log(this.hasSelector('someSelector.A'));
    console.log(this.hasSelector('someSelector.B'));
  }

  hasSelector(selector: Selectors) {
    return typeof this[selector] !== 'undefined'
  }
}
<!-- prints true for "A"; false for "B" -->
<div someSelector.A></div>

<!-- prints false for "A"; true for "B" -->
<div someSelector.B></div>

You could separate the directives into two and extend a root directive. This is really how directives should be used if you want to have different functionality for a different selector.

import { Directive } from '@angular/core';

@Directive({ selector: '*' })
export class DirectiveRoot implements OnInit {
  ngOnInit() {
    console.log(this);
    if (this instanceof DirectiveA) {
      console.log('Directive A!');
    }
  }
}

@Directive({ selector: '[someSelector.A]' })
export class DirectiveA extends DirectiveRoot {}

@Directive({ selector: '[someSelector.B]' })
export class DirectiveB extends DirectiveRoot {}
<!-- prints DirectiveA to the console -->
<div someSelector.A></div>

<!-- prints DirectiveB to the console -->
<div someSelector.B></div>

Another option is to look at the element itself and check to see if the attribute exists:

@Directive({ selector: '[someSelector.A], [someSelector.B]' })
export class DirectiveAB implements OnInit {
  constructor(private readonly ref: ViewContainerRef) {}

  ngOnInit() {
    console.log(this.ref.element.nativeElement.hasAttribute('someSelector.A'));
    console.log(this.ref.element.nativeElement.hasAttribute('someSelector.B'));
  }
}
<!-- prints true for "A"; false for "B" -->
<div someSelector.A></div>

<!-- prints false for "A"; true for "B" -->
<div someSelector.B></div>

CodePudding user response:

not really sure what you're trying to accomplish, but you can just check the inputs that match the selector in a few different ways

@Directive({
  selector: '[myDirective.A], [myDirective.B]',
})
export class MyDirective {
  @Input('myDirective.A')
  inputA;

  @Input('myDirective.B')
  inputB;

  constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { }

  ngOnInit() {
    if (this.inputA !== undefined) {
      // used directive A
    } else if (this.inputB !== undefined) {
      // used directive B
    }
  }
}

or your can check it in a setter:

@Directive({
  selector: '[myDirective.A], [myDirective.B]',
})
export class MyDirective {
  @Input('myDirective.A')
  set inputA(valueA) {
    // will only run if A selector used
  }

  @Input('myDirective.B')
  set inputB(valueB) {
    // will only run if B selector used
  }

  constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { }

}

in template:

<div *myDirective.A></div>
<div *myDirective.B></div>

https://stackblitz.com/edit/angular-92yazy?file=src/app/app.component.html

  • Related