Angular2中的事件代表团

时间:2016-07-06 11:04:19

标签: javascript typescript angular angular2-template

我正在用ng2开发一款应用,而且我正在努力解决问题。我正在构建一个日历,您可以在其中选择日期范围,我需要对click&日间单元格上的mouseenter/mouseleave个事件。所以我有一个像这样的代码(简化):

calendar.component.html

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

但是这给了我数百个独立的事件监听器在浏览器的记忆中(每天的单元格获得3个事件监听器,我一次最多可以显示12个月,所以它会结束1k听众)。

所以我想这样做&#34;正确的方式&#34;,使用名为&#34; event delegation&#34;的方法。我的意思是,在父组件(month)上附加一个click事件,当它收到click事件时,只需检查它是否发生在Day组件上 - 然后我才会对此点击作出反应。当你传递selector参数时,像jQuery这样的东西会on() method

但我是通过在处理程序代码中原生地引用DOM元素来实现的:

month.component.ts

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

并且我的同事拒绝了我的想法,因为 - 正如他们所说 - &#34;在NG2中必须有一种更简单,更恰当的方法来处理这个问题;就像在jQuery中一样。此外,它在这里失控 - 您在月份代码中对Day的点击做出反应。&#34;

所以,我的问题是,有更好的方法吗?或者我正在尝试解决一个我不应该再费心去解决的问题,因为用户&#39;设备每天获得越来越多的内存/处理能力?

提前致谢!

1 个答案:

答案 0 :(得分:1)

<强>简介

今天我偶然发现了这一点,我真的可以在很多应用程序中看到这种实现的必要性。现在我无法保证这是100%最好的技术,但是我已尽力使这种方法尽可能采用角度启发。

我提出的方法有两个阶段。第1阶段和第2阶段将共添加years * months + years * months * days,因此在1年内您将有12 + 365个事件。

<小时/> 舞台范围

第1阶段:委派事件,从点击一个月到实际的点击日期,而不需要当天的事件。
阶段2:将选定的日期传播回月份。

在深入研究之前,该应用程序包含3个按以下顺序嵌套的组件:app => month => day

这是所有必需的html。 app.component托管了几个月,month.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中嵌套了子元素,你需要为此实现冒泡,在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事件绑定到该元素,从而允许我们将其称为“本地”&#34;正如我们在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);
}

不幸的是,我们的回调函数在这里有点模棱两可的命名,但是如果另外命名的话,typescript会抱怨该属性不是CustomEventInit的定义对象文字。

<小时/> 多事件渠道

这种方法的另一个很酷的事情是你永远不必定义超过这个数量的事件,因为你可以通过这个委托汇集所有事件,然后在day.component.ts内运行逻辑来过滤{{1 }} ...

<强> month.component.ts

event.type

<强> day.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 }));
    }
  }