Angular 事件修饰符

源码

  • version 17.3.5
  • commit cc57d4c4998b4e38f940afdf358af37185028072

EventManagerPlugin

  • 对于官方来说,确实提供了一个事件管理的方案,可以根据任意的事件名进行不同的监听.
  • 但是,也只是根据事件名,监听DOM元素,处理他们的监听

官方未提供处理组件output的方案

  • 根据源码我们可知所有组件的output其实都是直接订阅的,也就是没有机会从中拦截

component output其实是被监听了两次?

  • 上面说了,所有组件都是直接订阅的.但是组件被直接订阅的同时,还进行了事件监听,也就是EventManagerPlugin也可以监听到,毕竟自定义组件,也是一个DOM元素
  • 但问题来了.即使可以通过创建事件管理器处理这个监听.但仅仅处理的是事件监听,订阅和这个八竿子打不着,正常的开发中,永远不会通过调用组件的DOM元素,进行dispatchEvent
  • 还有一个问题就是,事件管理器根据源码得知,addEventListener接口拿到的是元素+事件名.如何通过元素,找到对应的组件?

魔改时刻,炫技开始

  • 我们发现元素上有__ngContext__这个属性,这个属性上的数字其实就是LView id
  • 我们通过公开的非公开方法ɵgetLContext获得它的 LContext.然后读取到 LView.
  • 在 LView上,有许多关键属性,其中组件实例便是其中之一.索引位置
  • 找到了组件对应的元素,我们就可以 hook 掉原来的事件发射,让它只走我们自创的监听通道即可这部分比较简单,直接看源码

处理事件修饰符

  • 事件修饰符有两种,一种是guard返回为true就是禁止,另一种是map,转换数据用的
  • 代码中自带了一些guard类型的修饰符.来自Vue源码
  • 关于键盘类型的事件修饰符,完全调用ng官方的ɵKeyEventsPlugin

使用

  • 源码
  • npm i @cyia/ngx-common
// app module
import { EVENT_MODIFIER_OPTIONS, EventModifiersPlugin } from '@cyia/ngx-common/event';

  providers: [
    {
      provide: EVENT_MANAGER_PLUGINS,
      useClass: EventModifiersPlugin,
      multi: true,
      deps: [DOCUMENT],
    },
    {
      provide: EVENT_MODIFIER_OPTIONS,
      useValue: {
        modifiers: {
          map: {
            // 自定义修饰符
            prefix: (value) => {
              return `prefix:${value}`;
            },
          },
        },
        componentOutput: true,
      },
    },
  ],
<div (click)="clicked('divClicked')">
  <button (click.stop.once)="clicked('btnClicked')">点击</button>
</div>
<app-a (output2.prefix.once)="output($event)" (output2)="output($event)"></app-a>