27

I'm trying to figure out how I can access the selector that we pass into the @Component decorator.

For example

@Component({
  selector: 'my-component'
})
class MyComponent {
  constructor() {
     // I was hoping for something like the following but it doesn't exist
     this.component.selector // my-component
  }
}

Ultimately, I would like to use this to create a directive that automatically adds an attribute data-tag-name="{this.component.selector}" so that I can use Selenium queries to reliably find my angular elements by their selector.

I am not using protractor

luiscla27
  • 4,956
  • 37
  • 49
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217

4 Answers4

35

Use ElementRef:

import { Component, ElementRef } from '@angular/core'

@Component({
  selector: 'my-component'
})
export class MyComponent {
  constructor(elem: ElementRef) {
    const tagName = elem.nativeElement.tagName.toLowerCase();
  }
}
luiscla27
  • 4,956
  • 37
  • 49
Anton Poznyakovskiy
  • 2,109
  • 1
  • 20
  • 38
  • This probably is the only way to do it now. The older (now broken) way was better since it didn't require adding an injection and it would be accessible without requiring an instance, which I use when writing end to end tests to minimize duplication. – Ruan Mendes Mar 03 '17 at 14:19
9

OUTDATED See https://stackoverflow.com/a/42579760/227299

You need to get the metadata associated with your component:

Important Note Annotations get stripped out when you run the AOT compiler rendering this solution invalid if you are pre compiling templates

@Component({
  selector: 'my-component'
})
class MyComponent {
  constructor() {
    // Access `MyComponent` without relying on its name
    var annotations = Reflect.getMetadata('annotations', this.constructor);
    var componentMetadata = annotations.find(annotation => {
      return (annotation instanceof ComponentMetadata);
    });
    var selector = componentMetadata.selector // my-component
  }
}
luiscla27
  • 4,956
  • 37
  • 49
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • This looks promising, how is `Reflect.getMetadata` able to know that it's supposed to look for metadata for `MyComponent` if we're not telling it anything? I'm currently getting an error when I call `Reflect.getMetadata('annotations');` The error is `Reflect.getMetadata('annotations') VM61 angular2.sfx.dev.js:2652 Uncaught TypeError(…)getMetadata @ Reflect.getMetadata('annotations', this, 'annotations')` It looks like it needs two extra parameters – Ruan Mendes May 12 '16 at 13:31
  • 1
    Fixed your answer by passing `this.constructor` as the second argument. – Ruan Mendes May 12 '16 at 13:42
  • 2
    Please qualify this answer with the version of Angular 2 to which it applies - as of release 2.3.0 this seems to be out-of-date – Neoheurist Dec 14 '16 at 14:09
  • https://github.com/angular/angular/issues/13495#issuecomment-267314384 :-( – noelm Dec 22 '16 at 15:01
8

Here's an alternative if you need the selector name without access to the component's ElementRef:

const components = [MyComponent];

for (const component of components) {
  const selector = component.ɵcmp.selectors[0][0];
  console.log(selector);
}

Honestly, this first method feels rather hacky and who knows if this ɵ is supposed to just be for internal use? I thought I'd include it so that maybe someone could shed some light on it?

So, this is probably a safer route:

constructor(private factory: ComponentFactoryResolver) {
  const components = [MyComponent];

  for (const component of components) {
    const { selector } = this.factory.resolveComponentFactory(component);
    console.log(selector);
  }
}
Drew Payment
  • 81
  • 1
  • 2
  • 1
    The first solution depends on internal/private values. However, the second solution looks solid. – Ruan Mendes Jul 21 '21 at 12:16
  • 2
    Second option is definitely the most solid option I've seen so far. Thanks for pointing that out drew – maxime1992 Mar 09 '22 at 10:32
  • 1
    ComponentFactoryResolver is deprecated as of Angular v13. Which is a shame because I'm working with Angular elements and I could've used this to define the custom element in the AppModule constructor, so I can't inject the ViewContainerRef. – UKnoHowWeDo Apr 08 '22 at 14:16
4

Since Angualr v14.1 you can do this:

@Component({
  selector: 'my-component'
})
class MyComponent {
  constructor() {
     const metadata = reflectComponentType(MyComponent);
     const selector = metadata.selector // my-component
  }
}
Dmitry Efimenko
  • 10,973
  • 7
  • 62
  • 79