7

Consider the following template:

<my-component #ref></my-component>
<my-comp-2 [maxWidth]="ref.clientWidth"></my-comp-2>

My component doesn't have a .clientWidth property on it, but the element itself that hosts my component does.

Is there a name I could use or some other similar solution for getting a reference to the element itself in the HTML alone? For instance, I would like to do something like this:

<my-component #ref="element"></my-component>

The problem is that there is no directive with the exportAs option named "element". Nor is there any built-in equivalent documented on the Angular site.

I am aware that I could add clientWidth as a property, and that I could get a reference to the element within <my-component> class via ElementRef<HTMLElement> and dependency injection, but I would like a solution that involves HTML alone. Does such a solution exist?

2 Answers 2

9

I feel that your answer is the correct, but as curiosity, you can make a hard-work-around: create a directive like

@Directive({
  selector: '[element-directive]',
  exportAs: 'ngDirective',
})
export class MyDirective {
  constructor(public elementRef: ElementRef){}
}

And use

<my-component element-directive #ref="ngDirective">
</my-component>
<my-comp-2 [style.max-width]="ref.elementRef.nativeElement.offsetWidth+'px'">
</my-comp-2>

The other way, as you indicate, is in constructor of my-component, inject ElementRef and make it public

constructor(public elementRef:ElementRef){}

Then, you can use

<my-comp-2 [style.max-width]="ref.elementRef.nativeElement.offsetWidth+'px'">
</my-comp-2>

Or you can create any function in my-component that return what do you want and call this function

NOTE: I use style.max-width, I don't know if your my-comp2 has an input or not

Sign up to request clarification or add additional context in comments.

Comments

1

The DirectiveBinder class in the Angular compiler package appears to treat any non-named template reference as a reference to the component (if it is one) or the element (if it's not).

The relevant code is here:

  node.references.forEach(ref => {
    let dirTarget: DirectiveT|null = null;

    // If the reference expression is empty, then it matches the "primary" directive on the node
    // (if there is one). Otherwise it matches the host node itself (either an element or
    // <ng-template> node).
    if (ref.value.trim() === '') {
      // This could be a reference to a component if there is one.
      dirTarget = directives.find(dir => dir.isComponent) || null;
    } else {
      // This should be a reference to a directive exported via exportAs.
      dirTarget =
          directives.find(
              dir => dir.exportAs !== null && dir.exportAs.some(value => value === ref.value)) ||
          null;
      // Check if a matching directive was found.
      if (dirTarget === null) {
        // No matching directive was found - this reference points to an unknown target. Leave it
        // unmapped.
        return;
      }
    }

    if (dirTarget !== null) {
      // This reference points to a directive.
      this.references.set(ref, {directive: dirTarget, node});
    } else {
      // This reference points to the node itself.
      this.references.set(ref, node);
    }
  });

The first if branch is when no name is given (i.e. #ref or #ref="") and the else loops through any directives on the element being checked. Above this shows that no directives is a possibility, suggesting that there isn't some place where a special built-in "element" directive is being added.

Conclusion 1: If it's a component and there is no name in the template reference variable, then it will always refer to the component.

Conclusion 2: There are no special built-in directives in every case. Exceptions may exist for some standard HTML elements (e.g. <input>), but that doesn't help us here.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.