源码
- 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>