Angular2 中的事件委派
Event delegation in Angular2
我正在用ng2开发一个应用程序,但我正在努力解决一些问题。我正在构建一个日历,您可以在其中选择一个日期范围,我需要对日单元格上的click
和mouseenter/mouseleave
事件做出反应。所以我有一个这样的代码(简化):
日历组件.html
<month>
<day *ngFor="let day of days" (click)="handleClick()"
(mouseenter)="handleMouseEnter()"
(mouseleave)="handleMouseLeave()"
[innerHTML]="day"></day>
</month>
但这在浏览器的内存中给了我数百个单独的事件侦听器(每天的单元格有 3 个事件侦听器,我一次最多可以显示 12 个月,因此它将超过 1k 个侦听器)。
所以我想用"正确的方式"来做,使用称为"事件委托"的方法。我的意思是,在父组件(month
)上附加一个单击事件,当它收到单击事件时,只需检查它是否发生在Day
组件上 - 只有这样我才会对此单击做出反应。类似于 jQuery 在它的 on() 方法中执行的操作,当你传递给它 selector
参数时。
但我是通过在处理程序的代码中原生引用 DOM 元素来做到这一点的:
month.component.ts
private handleClick(event) {
if (event.target.tagName === 'DAY') {
// handle day click
} else {
// handle other cases
}
}
我的同事拒绝了我的想法,因为 - 正如他们所说 - "NG2 中必须有一种更简单、更合适的方法来处理这个问题;就像在jQuery中一样。此外,它在这里失控了 - 你对 Day 在 Month 代码中的点击做出反应。
所以,我的问题是,有没有更好的方法?还是我试图解决一个我不应该再费心解决的问题,因为用户的设备每天都在获得越来越多的内存/处理能力?
提前感谢!
简介
我今天偶然发现了这个,我真的可以看到在很多应用程序中对这种实现的需求。现在我不能保证这是 100% 最好的技术,但是我已经竭尽全力使这种方法尽可能有棱角。
我想出的方法有两个阶段。阶段 1 和阶段 2 将总共增加 years * months + years * months * days
,因此在 1 年内您将有 12 + 365
个事件。
阶段范围
第 1 阶段:将事件从单击一个月时委派到单击的实际日期,而无需当天的事件。
第 2 阶段:将所选日期传播回月份。
在深入研究之前,应用程序由 3 个组件组成,这些组件按以下顺序嵌套:app => month => day
这是所有必需的html.app.component托管月份,month.component托管天数,day.component除了将其日期显示为文本外,什么都不做。
app.component.html
<app-month *ngFor="let month of months" [data-month]="month"></app-month>
月.组件.html
<app-day *ngFor="let day of days" [data-day]="day">{{day}}</app-day>
天组件.html
<ng-content></ng-content>
这是相当股票标准的东西。
第 1 阶段
让我们看一下要从哪个month.component.ts
委派我们的活动。
// obtain a reference to the month(this) element
constructor(private element: ElementRef) { }
// when this component is clicked...
@HostListener('click', ['$event'])
public onMonthClick(event) {
// check to see whether the target element was a child or if it was in-fact this element
if (event.target != this.element.nativeElement) {
// if it was a child, then delegate our event to it.
// this is a little bit of javascript trickery where we are going to dispatch a custom event named 'delegateclick' on the target.
event.target.dispatchEvent(new CustomEvent('delegateEvent'));
}
}
在阶段 1 和阶段 2 中,只有 1 个警告,那就是; 如果你的day.component.html
中有嵌套的子元素,你将需要为此实现冒泡,在 if 语句中实现更好的逻辑,或者快速破解将是......在day.component.css
:host *{pointer-events: none;}
现在我们需要告诉我们的 day.component 期待我们的
delegateEvent
事件。因此,day.component.ts
您所要做的就是(以最有棱角的方式)是......
@HostListener('delegateEvent', ['$event'])
public onEvent() {
console.log("i've been clicked via a delegate!");
}
这是有效的,因为打字稿不关心事件是否是本机的,它只会将一个新的 javascript 事件绑定到元素,从而允许我们通过 event.target.dispatchEvent
"本机"调用它,就像我们在上面month.component.ts
中所做的那样。
第一阶段到此结束,我们现在成功地将事件从我们的月份委派到我们的天。
第 2 阶段
那么,如果我们说想要在 day.component
中的委托事件中运行一点逻辑,然后将其返回给 month.component
- 以便它可以以一种非常面向对象的方法继续使用自己的功能,会发生什么?幸运的是,我们可以很容易地实现这一点!
month.component.ts
更新以下内容。所有改变的是,我们现在将通过事件调用传递一个函数,并且我们定义了回调函数。
@HostListener('click', ['$event'])
public onMonthClick(event) {
if (event.target != this.element.nativeElement) {
event.target.dispatchEvent(new CustomEvent('delegateEvent', { detail: this.eventDelegateCallback}));
}
}
public eventDelegateCallback(data) {
console.log(data);
}
剩下的就是在day.component.ts
内调用这个函数......
public onEvent(event) {
// run whatever logic you like,
//return whatever data you like to month.component
event.detail(this.day);
}
不幸的是,我们的回调函数在这里命名有点模棱两可,但是打字稿会抱怨该属性不是CustomEventInit
定义的对象文字,如果以其他方式命名。
多事件漏斗
这种方法的另一个很酷的事情是,您永远不必定义超过此数量的事件,因为您可以通过此委托汇集所有事件,然后在day.component.ts
中运行逻辑以按event.type
过滤......
month.component.ts
@HostListener('click', ['$event'])
@HostListener('mouseover', ['$event'])
@HostListener('mouseout', ['$event'])
public onMonthEvent(event) {
if (event.target != this.element.nativeElement) {
event.target.dispatchEvent(new CustomEvent('delegateEvent', { detail: this.eventDelegateCallback }));
}
}
day.component.ts
private eventDelegateCallback: any;
@HostListener('delegateEvent', ['$event'])
public onEvent(event) {
this.eventDelegateCallback = event.detail;
if(event.type == "click"){
// run click stuff
this.eventDelegateCallback(this.day)
}
}
- 来自文档或下一个静态父级的事件委派
- jQuery中的事件委派,如何进行
- 为什么每次重新渲染父视图时都需要重新委派子视图的事件
- 什么对性能更好:每个元素的事件或一个具有委派的事件
- 修改鼠标事件对象的“目标”以进行事件委派
- 动态表行,将单击事件委派给新添加的元素
- 委派的事件处理程序选择器
- Javascript中的动态表和事件委派
- JQUERY的事件委派问题
- 如何用模块模式扩展javascript中的事件委派
- 锚上的委派点击事件
- 单击时事件不作为委派事件工作
- 不再需要视图后取消委派事件的最佳方法
- 如何仅处理子元素的委派事件
- jquery停止两次调用委派事件
- 使用jQuery获取委派事件中单击的元素
- 骨干视图委派事件获胜'不起作用
- 在jQuery中是否有委派事件的方法?
- 为什么event.stopPropagation()会停止委派事件中的多个匹配元素
- 通过分析for循环中的字符串来委派事件