You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.18 23:36:01