typescript - Como posso obter a hierarquia de visualização do Angular a partir de uma diretiva?
Versão 6+ do Angular
Estou trabalhando em uma diretiva que pode ser colocada em qualquer elemento para fins de registro de uso geral. Para contexto, seria algo como o seguinte:
@Directive({
selector: '[log]'
})
export class LogDirective {
@Input() log: string; // the log message
@Input() logOn: string; // event name to trigger logging
constructor(private renderer: Renderer2,
private elementRef: ElementRef) {
}
ngOnInit() {
this.renderer.listen(this.elementRef.nativeElement, this.logOn, () => {
// log the message, etc.
}
}
}
<div log="Clicked the element" logOn="click">Click Me</div>
Isso funciona bem, mas eu pensei que seria interessante obter automaticamente algum tipo de hierarquia de elementos pais para que isso pudesse ser registrado. Dessa forma, alguém poderia olhar o log e ver de maneira bem fácil de onde isso foi registrado. Algo como "AppComponent -> SomeOtherComponent -> ParentComponent". Existe algo que possa ser injetado nesta diretiva que consiga determinar onde ela está na hierarquia de visualização?
Resposta #1
você pode injetar ViewContainerRef na diretiva para obter acesso ao componente que a hospeda. você pode então usar isso para ler tanto o componente atual quanto o componente pai desse componente.
Fazer isso de forma recursiva vai te dar o resultado que você está procurando.
algo assim (vou deixar o algoritmo de recursão para você):
constructor(private viewContainerRef: ViewContainerRef) { }
private clicked() {
console.log(this.viewContainerRef['_view'].component);
console.log(this.viewContainerRef['_view'].parent.component);
console.log(this.viewContainerRef['_view'].parent.parent.component);
}
(1) Demonstração Stackblitz
(1)ignore o restante do aplicativo aí, eu pesquisei no Google por um "template de diretiva Angular" de alguém e o mudei para conseguir isso rapidamente
(2)claro, aviso, "você não deve ler _attributes no Angular porque ele pode ser alterado sem aviso em versões futuras e você não terá suporte para isso" etc, etc etc..
Resposta #2
De acordo com a especificação da web para elementos DOM personalizados. O nome da tag deve conter um hífen, e isso atua como um separador de namespace. Isso é necessário para proteger seus componentes da web para que eles não entrem em conflito com futuras alterações do HTML.
Portanto, qualquer elemento DOM pai que tenha um hífen é um componente. Assumindo que todos os elementos personalizados sejam componentes Angular e que seu projeto Angular use um prefixo conforme as diretrizes.
Você poderia usar uma função como esta para gerar o prefixo.
function getPrefix() {
let el = this.elementRef.native;
let names = [];
while (el) {
if(el.tagName.indexOf("-") !== -1) {
names.unshift(el.tagName);
}
el = el.parentNode;
}
return names.join(' -> ');
}
Então registre as mensagens assim:
ngOnInit() {
this.renderer.listen(this.elementRef.nativeElement, this.logOn, () => {
const prefix = this.getPrefix();
console.log(prefix + ' the message here');
}
}
