Como obter a referência do elemento no template Angular em vez da referência do componente

Sep 23 2020

Considere o seguinte modelo:

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

Meu componente não tem uma .clientWidthpropriedade, mas o próprio elemento que hospeda meu componente tem.

Existe um nome que eu poderia usar ou alguma outra solução semelhante para obter uma referência para o próprio elemento apenas no HTML? Por exemplo, eu gostaria de fazer algo assim:

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

O problema é que não existe uma diretiva com a exportAsopção chamada "elemento". Nem há qualquer equivalente embutido documentado no site Angular.

Estou ciente de que poderia adicionar clientWidthcomo uma propriedade e obter uma referência ao elemento dentro da <my-component>classe por meio de ElementRef<HTMLElement>injeção de dependência, mas gostaria de uma solução que envolvesse apenas HTML. Essa solução existe?

Respostas

1 Eliseo Sep 23 2020 at 01:51

Eu sinto que sua resposta está correta, mas como curiosidade , você pode dar um jeito de contornar: crie uma diretriz como

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

E use

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

A outra maneira, como você indicou, é no construtor de my-component, injete ElementRef e torne-o público

constructor(public elementRef:ElementRef){}

Então, você pode usar

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

Ou você pode criar qualquer função em my-component que retorne o que você quer e chame esta função

NOTA: Eu uso style.max-width, não sei se seu my-comp2 tem uma entrada ou não

KevinBeal Sep 23 2020 at 01:05

A DirectiveBinder classe no pacote do compilador Angular parece tratar qualquer referência de template não nomeada como uma referência ao componente (se for um) ou ao elemento (se não for).

O código relevante está aqui:

  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);
    }
  });

O primeiro if branch é quando nenhum nome é fornecido ( #refou seja, ou #ref="") e o else faz um loop por quaisquer diretivas no elemento que está sendo verificado. Acima isso mostra que nenhuma diretiva é uma possibilidade, sugerindo que não há algum lugar onde uma diretiva de "elemento" embutida especial está sendo adicionada.

Conclusão 1: Se for um componente e não houver nome na variável de referência do template, então sempre fará referência ao componente.

Conclusão 2: Não há diretivas internas especiais em todos os casos. Podem existir exceções para alguns elementos HTML padrão (por exemplo <input>), mas isso não nos ajuda aqui.