2016-07-06 61 views
13

我正在ng2中开发一个应用程序,而且我正在努力寻找一些东西。我正在制作一个日历,您可以选择一个日期范围,并且需要对日间单元格上的事件做出反应。所以我有一个代码(简化)这样的:Angular2中的事件代表团

calendar.component.html

<month> 
    <day *ngFor="let day of days" (click)="handleClick()" 
     (mouseenter)="handleMouseEnter()" 
     (mouseleave)="handleMouseLeave()" 
     [innerHTML]="day"></day> 
</month> 

但是这给了我几百个单独的事件监听器在浏览器的内存(每一天的细胞得到3个事件监听器,我可以一次显示12个月的时间,所以它会超过1K的听众)。

所以我想用“事件委托”的方法做到“正确的方式”。我的意思是,在父组件上附加一个点击事件(month),当它收到点击事件时,只需检查它是否发生在Day组件上 - 然后我会对此点击作出反应。当你传递参数selector时,像jQuery那样在on() method中。

但是我被本地引用DOM元素的处理程序的代码,这样做:

month.component.ts

private handleClick(event) { 
    if (event.target.tagName === 'DAY') { 
     // handle day click 
    } else { 
     // handle other cases 
    } 
} 

和我的同事拒绝了我的想法,因为 - 因为他们说“在NG2中必须有一种更简单,适当的方式来处理这个问题,就像jQuery中的一样,此外,它在这里失控 - 你对Day的代码中Day的点击做出了反应。

所以,我的问题是,有没有更好的方法?或者我正试图解决一个我不应该再烦恼的问题,因为用户的设备每天都会获得越来越多的内存/处理能力?

在此先感谢!

+1

有独立的模块,其处理此。我发现“dom-delegate”工作得很好。就我所知,在ng2中没有内置任何东西。 – Chrillewoodz

+1

如果这是一个问题......这将是一个角度优化问题。您不必担心汇总事件以进行优化。 –

+1

几个月前我遇到了类似的问题,据我所知,您所描述的方法是以原始角度执行此操作的最佳方法。使用较少的事件侦听器检查点击目标。我建议的唯一的事情就是把逻辑放在一个服务或指令中(指令可能会更好,因为你需要每个月div),这样代码是干净的并且与组件分离。毕竟,它是html逻辑,而不是组件逻辑 – FussinHussin

回答

1

介绍

我今天这个偶然发现,我真的可以看到在很多应用中,这种执行的需要。现在我不能保证这是100%最好的技术,但是我尽量让这种方法尽可能地具有灵活性。

我提出的方法有两个阶段。第一阶段和第二阶段将增加总数为years * months + years * months * days,因此在1年内您将有12 + 365事件。


舞台范围

第一阶段:当一个月下来点击进入实际的一天代表事件,被点击,而无需在当天的事件。
阶段2:将选定的日期传播回月份。

只是在深入研究之前,该应用程序由被嵌套在下面的顺序3个组成部分:app => month => day


这是所有需要的HTML。应用程序。组件托管几个月,month.component托管几天,day.component不做任何事情,而是以文本形式显示它的一天。

app.component.html

<app-month *ngFor="let month of months" [data-month]="month"></app-month> 

month.component.html

<app-day *ngFor="let day of days" [data-day]="day">{{day}}</app-day> 

day.component.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内子元素,你要么需要实现冒泡为此,更好的逻辑,如果陈述,或快速黑客会。在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中做的那样。

第1阶段即将结束,我们现在已成功地将我们的月份活动委托给我们的日子。


第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) 
    } 
    } 
+0

要么我不理解你的概念,要么你误解了我试图实现的目标。我想只有N个事件监听器,其中N是个月数。您的方法创建与DayComponents上的HostBinding单击事件完全相同的事件侦听器数量。只是你绑定了自定义事件。 –

+0

然而,多项活动确实可以带来利润:) –

+0

@MichalLeszczyk它并不完全符合N标记,但是是一项重大改进,并且在添加新事件类型时也不会呈指数增长。这不是完美的,但我认为它是在正确的轨道上! – Zze