malarres
malarres

Reputation: 2946

Know the html a component is being called from

In the .ts files of an Angular5 project I needed to know the name of the html that was adding the component (for the sake of creating unique names for some buttons inside those components) So my html has ended being something along the lines of:

<my-component data-myattribute="thishtmlfile"  ></my-component>

and then I used @Input on the .ts side to grab this value

But I was wondering if there is a way to automatically know the name of the html my component is being called from, inside the code of the component itself.

The Reflect object didn't work well for me, but I'm open to try again ;)

Upvotes: 0

Views: 250

Answers (1)

user4676340
user4676340

Reputation:

I have made something similar to give my HTML elements unique IDs. Here it is, if it can be of any help :

import { AfterViewInit, Directive, ElementRef, Input, Renderer2 } from '@angular/core';

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[app-path]'
})
export class AppPathDirective implements AfterViewInit {

  @Input('app-path') path: string & string[];

  @Input('app-path.ignoreWarning') private _ignoreWarning = false;

  get shouldWarn() {
    return this._ignoreWarning === false;
  }

  element: HTMLElement;

  constructor(
    private renderer: Renderer2,
    el: ElementRef<HTMLElement>,
  ) {
    this.element = el.nativeElement;

    if (this.element.id) {
      if (this.shouldWarn) {
        console.warn(`Custom business error`);
      }
    }
  }

  ngAfterViewInit() {
    this.setElementCustomId();
  }

  setElementCustomId() {
    let name = '';

    // Crawl up to the parent feature with the renderer
    let currentElement: HTMLElement = this.element;
    while (currentElement && currentElement.tagName && !currentElement.tagName.toLowerCase().startsWith('app-')) {
      currentElement = this.renderer.parentNode(currentElement);
    }

    if (currentElement && currentElement.tagName) {
      name += currentElement.tagName.toLowerCase()
      // remove 'app-'
        .replace('app-', '') + '-';
    } else {
      if (this.shouldWarn) {
        console.warn(`Custom error : No parent feature tag has been found : the name of the feature won't appear in the ID`);
      }
    }

    // Append the HTML component type to the name
    const htmlElementName: string = this.element.constructor.name.toLowerCase();
    if (!htmlElementName.includes('html') || !htmlElementName.includes('element')) {
      // If it is not an HTML element, do nothing and throw an error
      if (this.shouldWarn) {
        console.warn(`Custom error : No HTML Element was found for the app-path : the element type won't appear in the ID`);
      }
    } else if (htmlElementName === 'htmlelement') {
      // Custom Angular / Material element : ignore.
    } else {
      // Else, append the element type
      name += htmlElementName.replace(/(html|element)/g, '');
    }

    // Append the custom suffix to the name
    let customSuffixe = '';
    if (typeof this.path === 'string' && this.path) {
      customSuffixe += '-' + this.path;
    } else if (this.path && this.path.length) {
      this.path.forEach(item => customSuffixe += '-' + item);
    }
    name += customSuffixe;

    this.renderer.setAttribute(this.element, 'id', name);
  }
}

This creates a custom name based on the current component you're in, the HTML element targeted by your directive, and some custom suffix you can append to the directive.

Upvotes: 1

Related Questions