Skip to content

Commit 104cb85

Browse files
committed
Modify event emitters to limit change detection runs
1 parent 7952f7c commit 104cb85

3 files changed

Lines changed: 78 additions & 33 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
11

2+
<a name="2.0.0-beta.2"></a>
3+
# 2.0.0-beta.2 (2018-11-19)
4+
5+
* **Performance:** Make `@Output()` event emitters (`dragStart`, `dragProgress`, `dragEnd`, `gutterClick`, `transitionEnd`) works "lazily" to avoid useless change detection runs, especially for `dragProgress` which could be costly in big app.
6+
7+
8+
<a name="2.0.0-beta.1"></a>
9+
# 2.0.0-beta.1 (2018-11-17)
10+
11+
* **Styles:** Refactor the way styles are manage, no more `renderer.setStyle()` everywhere (except for areas `order` & `flex-basis`), now works with added/removed classes (`is-horizontal`/`is-vertical`, `is-disabled`, `is-transition`, `is-disabled`, `is-dragging`), way better. Doing like this, `<as-split-gutter>` directive is not needed anymore.
12+
13+
214
<a name="1.0.4"></a>
315
# 1.0.4 (2018-11-15)
416

projects/angular-split/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-split",
3-
"version": "1.0.4",
3+
"version": "2.0.0-beta.2",
44
"description": "Angular UI library used to split views and to allow dragging to resize the split areas using CSS flexbox layout.",
55
"author": "bertrandg",
66
"repository": {

projects/angular-split/src/lib/component/split.component.ts

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Component, Input, Output, HostBinding, ChangeDetectionStrategy, ChangeDetectorRef, EventEmitter, Renderer2, AfterViewInit, OnDestroy, ElementRef, NgZone } from '@angular/core';
2-
import { Subject, Observable } from 'rxjs';
1+
import { Component, Input, Output, HostBinding, ChangeDetectionStrategy, ChangeDetectorRef, Renderer2, AfterViewInit, OnDestroy, ElementRef, NgZone } from '@angular/core';
2+
import { Observable, Subscriber } from 'rxjs';
33
import { debounceTime } from 'rxjs/operators';
44

55
import { IArea } from '../interface/IArea';
@@ -50,7 +50,7 @@ import { getPointFromEvent, getPixelSize } from '../utils';
5050
class="as-split-gutter"
5151
[style.flex-basis.px]="gutterSize"
5252
[style.order]="index*2+1"
53-
(click)="clickGutter($event, index*2+1, index+1)"
53+
(click)="clickGutter($event, index+1)"
5454
(mousedown)="startDragging($event, index*2+1, index+1)"
5555
(touchstart)="startDragging($event, index*2+1, index+1)"></div>
5656
</ng-template>`,
@@ -137,15 +137,29 @@ export class SplitComponent implements AfterViewInit, OnDestroy {
137137

138138
////
139139

140-
@Output() dragStart = new EventEmitter<{gutterNum: number, sizes: Array<number>}>(false);
141-
@Output() dragProgress = new EventEmitter<{gutterNum: number, sizes: Array<number>}>(false);
142-
@Output() dragEnd = new EventEmitter<{gutterNum: number, sizes: Array<number>}>(false);
143-
@Output() gutterClick = new EventEmitter<{gutterNum: number, sizes: Array<number>}>(false);
140+
private _dragStartSubscriber: Subscriber<{gutterNum: number, sizes: Array<number>}>
141+
@Output() get dragStart(): Observable<{gutterNum: number, sizes: Array<number>}> {
142+
return new Observable(subscriber => this._dragStartSubscriber = subscriber);
143+
}
144+
private _dragProgressSubscriber: Subscriber<{gutterNum: number, sizes: Array<number>}>
145+
@Output() get dragProgress(): Observable<{gutterNum: number, sizes: Array<number>}> {
146+
return new Observable(subscriber => this._dragProgressSubscriber = subscriber);
147+
}
148+
private _dragEndSubscriber: Subscriber<{gutterNum: number, sizes: Array<number>}>
149+
@Output() get dragEnd(): Observable<{gutterNum: number, sizes: Array<number>}> {
150+
return new Observable(subscriber => this._dragEndSubscriber = subscriber);
151+
}
152+
private _gutterClickSubscriber: Subscriber<{gutterNum: number, sizes: Array<number>}>
153+
@Output() get gutterClick(): Observable<{gutterNum: number, sizes: Array<number>}> {
154+
return new Observable(subscriber => this._gutterClickSubscriber = subscriber);
155+
}
144156

145-
private transitionEndInternal = new Subject<Array<number>>();
146-
@Output() transitionEnd: Observable<Array<number>> = this.transitionEndInternal.asObservable().pipe(
147-
debounceTime(20)
148-
);
157+
private _transitionEndSubscriber: Subscriber<Array<number>>
158+
@Output() get transitionEnd(): Observable<Array<number>> {
159+
return new Observable(subscriber => this._transitionEndSubscriber = subscriber).pipe(
160+
debounceTime<Array<number>>(20)
161+
);
162+
}
149163

150164
@HostBinding('style.min-width') get cssMinwidth() {
151165
return (this.direction === 'horizontal') ? `${ this.getNbGutters() * this.gutterSize }px` : null;
@@ -350,7 +364,7 @@ export class SplitComponent implements AfterViewInit, OnDestroy {
350364
});
351365
}
352366

353-
public clickGutter(event: MouseEvent, gutterOrder: number, gutterNum: number): void {
367+
public clickGutter(event: MouseEvent, gutterNum: number): void {
354368
if(this.startPoint && this.startPoint.x === event.pageX && this.startPoint.y === event.pageY) {
355369
this.currentGutterNum = gutterNum;
356370

@@ -373,21 +387,18 @@ export class SplitComponent implements AfterViewInit, OnDestroy {
373387
return;
374388
}
375389

376-
this.ngZone.runOutsideAngular(() => {
377-
this.dragListeners.push( this.renderer.listen('document', 'mouseup', (e: MouseEvent) => this.stopDragging()) );
378-
this.dragListeners.push( this.renderer.listen('document', 'touchend', (e: TouchEvent) => this.stopDragging()) );
379-
this.dragListeners.push( this.renderer.listen('document', 'touchcancel', (e: TouchEvent) => this.stopDragging()) );
380-
});
381-
382390
this.dragStartValues.sizePixelContainer = getPixelSize(this.elRef, this.direction);
383391
this.dragStartValues.sizePixelA = getPixelSize(areaA.component.elRef, this.direction);
384392
this.dragStartValues.sizePixelB = getPixelSize(areaB.component.elRef, this.direction);
385393
this.dragStartValues.sizePercentA = areaA.size;
386394
this.dragStartValues.sizePercentB = areaB.size;
387-
388395
this.currentGutterNum = gutterNum;
389396

390397
this.ngZone.runOutsideAngular(() => {
398+
this.dragListeners.push( this.renderer.listen('document', 'mouseup', this.stopDragging.bind(this)) );
399+
this.dragListeners.push( this.renderer.listen('document', 'touchend', this.stopDragging.bind(this)) );
400+
this.dragListeners.push( this.renderer.listen('document', 'touchcancel', this.stopDragging.bind(this)) );
401+
391402
this.dragListeners.push( this.renderer.listen('document', 'mousemove', (e: MouseEvent) => this.dragEvent(e, areaA, areaB)) );
392403
this.dragListeners.push( this.renderer.listen('document', 'touchmove', (e: TouchEvent) => this.dragEvent(e, areaA, areaB)) );
393404
});
@@ -471,7 +482,11 @@ export class SplitComponent implements AfterViewInit, OnDestroy {
471482
}
472483
}
473484

474-
private stopDragging(): void {
485+
private stopDragging(event?: Event): void {
486+
if(event) {
487+
event.preventDefault();
488+
}
489+
475490
if(this.isDragging === false) {
476491
return;
477492
}
@@ -495,25 +510,43 @@ export class SplitComponent implements AfterViewInit, OnDestroy {
495510
this.renderer.removeClass(this.elRef.nativeElement, 'is-dragging');
496511

497512
// Needed to let (click)="clickGutter(...)" event run and verify if mouse moved or not
498-
setTimeout(() => {
499-
this.startPoint = null;
500-
this.endPoint = null;
501-
})
513+
this.ngZone.runOutsideAngular(() => {
514+
setTimeout(() => {
515+
this.startPoint = null;
516+
this.endPoint = null;
517+
})
518+
});
502519
}
503520

504521

505522
public notify(type: 'start' | 'progress' | 'end' | 'click' | 'transitionEnd'): void {
506523
const sizes: Array<number> = this.displayedAreas.map(a => a.size * 100);
507524

508-
this.ngZone.run(() => {
509-
switch(type) {
510-
case 'start': return this.dragStart.emit({gutterNum: this.currentGutterNum, sizes});
511-
case 'progress': return this.dragProgress.emit({gutterNum: this.currentGutterNum, sizes});
512-
case 'end': return this.dragEnd.emit({gutterNum: this.currentGutterNum, sizes});
513-
case 'click': return this.gutterClick.emit({gutterNum: this.currentGutterNum, sizes});
514-
case 'transitionEnd': return this.transitionEndInternal.next(sizes);
525+
if(type === 'start') {
526+
if(this._dragStartSubscriber) {
527+
this.ngZone.run(() => this._dragStartSubscriber.next({gutterNum: this.currentGutterNum, sizes}));
515528
}
516-
});
529+
}
530+
else if(type === 'progress') {
531+
if(this._dragProgressSubscriber) {
532+
this.ngZone.run(() => this._dragProgressSubscriber.next({gutterNum: this.currentGutterNum, sizes}));
533+
}
534+
}
535+
else if(type === 'end') {
536+
if(this._dragEndSubscriber) {
537+
this.ngZone.run(() => this._dragEndSubscriber.next({gutterNum: this.currentGutterNum, sizes}));
538+
}
539+
}
540+
else if(type === 'click') {
541+
if(this._gutterClickSubscriber) {
542+
this.ngZone.run(() => this._gutterClickSubscriber.next({gutterNum: this.currentGutterNum, sizes}));
543+
}
544+
}
545+
else if(type === 'transitionEnd') {
546+
if(this._transitionEndSubscriber) {
547+
this.ngZone.run(() => this._transitionEndSubscriber.next(sizes));
548+
}
549+
}
517550
}
518551

519552
public ngOnDestroy(): void {

0 commit comments

Comments
 (0)