Skip to content

Commit 823ee4d

Browse files
committed
perf(click): lazily initialise all click handlers
Fixes #942
1 parent 31fda3b commit 823ee4d

File tree

3 files changed

+61
-13
lines changed

3 files changed

+61
-13
lines changed

projects/angular-calendar/src/modules/common/click.directive.ts

+28-13
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,24 @@ import {
99
Inject
1010
} from '@angular/core';
1111
import { DOCUMENT } from '@angular/common';
12+
import { Observable, Subject } from 'rxjs';
13+
import { requestIdleCallbackObservable } from './request-idle-callback';
14+
import { switchMapTo, takeUntil } from 'rxjs/operators';
1215

1316
const clickElements = new Set<HTMLElement>();
1417

18+
const eventName: string =
19+
typeof window !== 'undefined' && typeof window['Hammer'] !== 'undefined'
20+
? 'tap'
21+
: 'click';
22+
1523
@Directive({
1624
selector: '[mwlClick]'
1725
})
1826
export class ClickDirective implements OnInit, OnDestroy {
1927
@Output('mwlClick') click = new EventEmitter<MouseEvent>(); // tslint:disable-line
2028

21-
private removeListener: () => void;
29+
private destroy$ = new Subject();
2230

2331
constructor(
2432
private renderer: Renderer2,
@@ -33,16 +41,16 @@ export class ClickDirective implements OnInit, OnDestroy {
3341
'true'
3442
);
3543
clickElements.add(this.elm.nativeElement);
36-
const eventName: string =
37-
typeof window !== 'undefined' && typeof window['Hammer'] !== 'undefined'
38-
? 'tap'
39-
: 'click';
40-
this.removeListener = this.renderer.listen(
41-
this.elm.nativeElement,
42-
eventName,
43-
event => {
44+
45+
// issue #942 - lazily initialise all click handlers after initial render as hammerjs is slow
46+
requestIdleCallbackObservable()
47+
.pipe(
48+
switchMapTo(this.listen()),
49+
takeUntil(this.destroy$)
50+
)
51+
.subscribe(event => {
4452
// prevent child click events from firing on parent elements that also have click events
45-
let nearestClickableParent: HTMLElement = event.target;
53+
let nearestClickableParent = event.target as HTMLElement;
4654
while (
4755
!clickElements.has(nearestClickableParent) &&
4856
nearestClickableParent !== this.document.body
@@ -54,12 +62,19 @@ export class ClickDirective implements OnInit, OnDestroy {
5462
if (isThisClickableElement) {
5563
this.click.next(event);
5664
}
57-
}
58-
);
65+
});
5966
}
6067

6168
ngOnDestroy(): void {
62-
this.removeListener();
69+
this.destroy$.next();
6370
clickElements.delete(this.elm.nativeElement);
6471
}
72+
73+
private listen() {
74+
return new Observable<MouseEvent>(observer => {
75+
return this.renderer.listen(this.elm.nativeElement, eventName, event => {
76+
observer.next(event);
77+
});
78+
});
79+
}
6580
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Observable } from 'rxjs';
2+
3+
const isSupported =
4+
typeof window !== 'undefined' &&
5+
typeof window['requestIdleCallback'] !== 'undefined';
6+
7+
export function requestIdleCallbackObservable() {
8+
return new Observable(observer => {
9+
/* istanbul ignore else */
10+
if (isSupported) {
11+
const id = window['requestIdleCallback'](() => {
12+
observer.next();
13+
observer.complete();
14+
});
15+
return () => {
16+
window['cancelIdleCallback'](id);
17+
};
18+
} else {
19+
const timeoutId = setTimeout(() => {
20+
observer.next();
21+
observer.complete();
22+
}, 1);
23+
return () => {
24+
clearTimeout(timeoutId);
25+
};
26+
}
27+
});
28+
}

projects/angular-calendar/test/entry.ts

+5
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,17 @@ import * as sinon from 'sinon';
2323
use(sinonChai);
2424

2525
let rafStub: sinon.SinonStub;
26+
let requestIdleCallbackStub: sinon.SinonStub;
2627
beforeEach(() => {
2728
rafStub = sinon.stub(window, 'requestAnimationFrame').callsArg(0);
29+
requestIdleCallbackStub = sinon
30+
.stub(window, 'requestIdleCallback' as any)
31+
.callsArg(0);
2832
});
2933

3034
afterEach(() => {
3135
rafStub.restore();
36+
requestIdleCallbackStub.restore();
3237
});
3338

3439
// First, initialize the Angular testing environment.

0 commit comments

Comments
 (0)