Intro
我今天偶然发现了这一点,我确实可以在很多应用程序中看到这种实现的必要性。现在我不能保证这是 100% 最好的技术,但是我已经竭尽全力使这种方法尽可能地受到角度启发。
我提出的方法有两个阶段。第一阶段和第二阶段总共将增加years * months + years * months * days
,所以一年内你将拥有12 + 365
事件。
Stage Scope
Stage 1:将单击一个月时的事件委托到单击的实际日期,而不需要当天发生事件。
Stage 2:将所选日期传播回月份。
在深入研究之前,该应用程序由 3 个组件组成,这些组件按以下顺序嵌套:app => month => day
这就是所需的全部 html。 app.component 托管多个月份,month.component 托管多个天,而 day.component 不执行任何操作,只是将日期显示为文本。
应用程序组件.html
<app-month *ngFor="let month of months" [data-month]="month"></app-month>
月.component.html
<app-day *ngFor="let day of days" [data-day]="day">{{day}}</app-day>
day.component.html
<ng-content></ng-content>
这是相当标准的东西。
Stage 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 中,都有only1 个警告,即;如果您的子元素中嵌套了day.component.html
,您需要为此实现冒泡,在 if 语句中实现更好的逻辑,或者快速破解......day.component.css
:host *{pointer-events: none;}
Now we need to tell our day.component to be expecting our
delegateEvent
event. So in
day.component.ts
all you have to do (in the most angular way possible) is...
@HostListener('delegateEvent', ['$event'])
public onEvent() {
console.log("i've been clicked via a delegate!");
}
这是有效的,因为 typescript 不关心事件是否是本机的,它只会将一个新的 javascript 事件绑定到元素,从而允许我们通过“本机”调用它event.target.dispatchEvent
正如我们上面所做的month.component.ts
.
第一阶段到此结束,我们现在成功地将每月的活动委托给每天的活动。
Stage 2
那么,如果我们说想要在委托事件中运行一点逻辑,会发生什么?day.component
然后将其返回到month.component
- 这样它就可以以非常面向对象的方法继续执行自己的功能?幸运的是,我们可以很容易地实现这一点!
In 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
如果另有命名。
Multiple Event Funnel
这种方法的另一个很酷的事情是,您永远不必定义超过此数量的事件,因为您可以通过此委托汇集所有事件,然后在其中运行逻辑day.component.ts
过滤依据event.type
...
月份.组件.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 }));
}
}
日组件.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)
}
}