Angular9自定义指令的@HostListener点击事件无法触发怎么解决?
问题根因
你遇到的事件不触发问题核心原因是:你在accordion组件中没有使用<ng-content>投影用户编写的子元素,而是提取了子元素关联的模板单独重新渲染,导致原本挂载了@HostListener的指令宿主元素从未被插入DOM,绑定的事件自然无法触发:
- 挂载
accordionItem指令的<div accordionItem>仅被你通过@ContentChildren读取了指令实例,本身没有渲染到DOM中,所以它的点击监听不生效 *accordionItemHeader是结构指令,Angular会把它解析为<ng-template accordionItemHeader>包裹你编写的header内容,这个<ng-template>是指令的宿主,本身不会渲染到DOM,你单独渲染模板内部内容时,内容元素上没有绑定指令的点击事件,所以也无法触发。
解决方案
调整组件逻辑,让各指令自己负责对应内容的渲染,保留原始宿主元素的DOM存在即可,不需要修改你现有的组件使用代码:
第一步:修改Accordion组件模板
不需要自己遍历渲染header和body模板,直接投影内容即可:
<!-- accordion.component.html --> <div class="accordion-wrapper"> <ng-content></ng-content> </div>
第二步:修改AccordionItemHeaderDirective
让结构指令自己渲染模板内容,保证元素插入DOM:
import { Directive, TemplateRef, ViewContainerRef, OnInit, HostListener } from '@angular/core'; @Directive({ selector: "[accordionItemHeader]", }) export class AccordionItemHeaderDirective implements OnInit { constructor( private template: TemplateRef<any>, private viewContainer: ViewContainerRef ) {} ngOnInit() { // 主动渲染模板内容到当前位置 this.viewContainer.createEmbeddedView(this.template); } @HostListener('click', ['$event.target']) onClick() { console.log("click header"); // 可通过Output对外暴露事件,实现自定义点击回调 } }
第三步:修改AccordionItemDirective
自身控制body的展开收起,宿主元素会被投影到DOM中,点击监听正常生效:
import { Directive, Input, ContentChild, AfterContentInit, HostListener } from '@angular/core'; import { AccordionItemBodyDirective } from './accordion-item-body.directive'; @Directive({ selector: "[accordionItem]", }) export class AccordionItemDirective implements AfterContentInit { @Input() expanded: boolean = false; @ContentChild(AccordionItemBodyDirective) body!: AccordionItemBodyDirective; ngAfterContentInit() { // 初始化展开状态 this.updateBodyVisible(); } @HostListener('click') onClick() { console.log("click item"); this.expanded = !this.expanded; this.updateBodyVisible(); } private updateBodyVisible() { this.body.viewContainer.clear(); if (this.expanded) { this.body.viewContainer.createEmbeddedView(this.body.template); } } }
第四步:修改AccordionItemBodyDirective
只需要暴露模板和视图容器供父指令调用即可:
import { Directive, TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ selector: "[accordionItemBody]", }) export class AccordionItemBodyDirective { constructor( public template: TemplateRef<any>, public viewContainer: ViewContainerRef ) {} }
修改完成后点击header即可触发对应的点击监听函数,不需要额外套div包裹header元素。
内容的提问来源于stack exchange,提问作者Jan Vladimir Mostert
相关产品推荐
相关产品推荐

