From 6441d260a5fa8809584140373b2dce485be56455 Mon Sep 17 00:00:00 2001 From: Joschua Schneider Date: Thu, 8 Jul 2021 19:09:16 +0200 Subject: [PATCH] fix(datepicker): update subscriptions of datepicker-inline and daterangepicker-inline Introduce updateSubscriptions method to unsubscribe from previous subscriptions and to create new subscriptions on new _datepickerRef.instance Reduce calls to setConfig by introducing a shouldSetConfig flag, thereby batching complex updates together Closes #5888 where EventEmitter does not fire after Input changes --- .../bs-datepicker-inline.component.ts | 63 ++++++++++------- .../bs-daterangepicker-inline.component.ts | 70 +++++++++++-------- 2 files changed, 81 insertions(+), 52 deletions(-) diff --git a/src/datepicker/bs-datepicker-inline.component.ts b/src/datepicker/bs-datepicker-inline.component.ts index 596c349936..f08152642f 100644 --- a/src/datepicker/bs-datepicker-inline.component.ts +++ b/src/datepicker/bs-datepicker-inline.component.ts @@ -64,7 +64,7 @@ export class BsDatepickerInlineDirective implements OnInit, OnDestroy, OnChanges * Emits when datepicker value has been changed */ @Output() bsValueChange: EventEmitter = new EventEmitter(); - protected _subs: Subscription[] = []; + protected _subs?: Subscription; private readonly _datepicker: ComponentLoader; private _datepickerRef?: ComponentRef; @@ -110,24 +110,7 @@ export class BsDatepickerInlineDirective implements OnInit, OnDestroy, OnChanges ngOnInit(): void { this.setConfig(); - - // if date changes from external source (model -> view) - this._subs.push( - this.bsValueChange.subscribe((value: Date) => { - if (this._datepickerRef) { - this._datepickerRef.instance.value = value; - } - }) - ); - - // if date changes from picker (view -> model) - if (this._datepickerRef) { - this._subs.push( - this._datepickerRef.instance.valueChange.subscribe((value: Date) => { - this.bsValue = value; - }) - ); - } + this.updateSubscriptions(); } ngOnChanges(changes: SimpleChanges): void { @@ -135,42 +118,72 @@ export class BsDatepickerInlineDirective implements OnInit, OnDestroy, OnChanges return; } + let shouldSetConfig = false; + if (changes.minDate) { this._datepickerRef.instance.minDate = this.minDate; - this.setConfig(); + shouldSetConfig = true; } if (changes.maxDate) { this._datepickerRef.instance.maxDate = this.maxDate; - this.setConfig(); + shouldSetConfig = true; } if (changes.datesDisabled) { this._datepickerRef.instance.datesDisabled = this.datesDisabled; - this.setConfig(); + shouldSetConfig = true; } if (changes.datesEnabled) { this._datepickerRef.instance.datesEnabled = this.datesEnabled; this._datepickerRef.instance.value = this._bsValue; + shouldSetConfig = true; } if (changes.isDisabled) { this._datepickerRef.instance.isDisabled = this.isDisabled; - this.setConfig(); + shouldSetConfig = true; } if (changes.dateCustomClasses) { this._datepickerRef.instance.dateCustomClasses = this.dateCustomClasses; - this.setConfig(); + shouldSetConfig = true; } if (changes.dateTooltipTexts) { this._datepickerRef.instance.dateTooltipTexts = this.dateTooltipTexts; + shouldSetConfig = true; + } + + if (shouldSetConfig) { this.setConfig(); } } + updateSubscriptions(): void { + this._subs?.unsubscribe(); + this._subs = new Subscription(); + + // if date changes from external source (model -> view) + this._subs.add( + this.bsValueChange.subscribe((value: Date) => { + if (this._datepickerRef) { + this._datepickerRef.instance.value = value; + } + }) + ); + + // if date changes from picker (view -> model) + if (this._datepickerRef) { + this._subs.add( + this._datepickerRef.instance.valueChange.subscribe((value: Date) => { + this.bsValue = value; + }) + ); + } + } + /** * Set config for datepicker */ @@ -196,6 +209,8 @@ export class BsDatepickerInlineDirective implements OnInit, OnDestroy, OnChanges .attach(BsDatepickerInlineContainerComponent) .to(this._elementRef) .show(); + + this.updateSubscriptions(); } ngOnDestroy() { diff --git a/src/datepicker/bs-daterangepicker-inline.component.ts b/src/datepicker/bs-daterangepicker-inline.component.ts index 3ee194277f..7c8593912a 100644 --- a/src/datepicker/bs-daterangepicker-inline.component.ts +++ b/src/datepicker/bs-daterangepicker-inline.component.ts @@ -69,7 +69,7 @@ export class BsDaterangepickerInlineDirective implements OnInit, OnDestroy, OnCh */ @Output() bsValueChange: EventEmitter = new EventEmitter(); - protected _subs: Subscription[] = []; + protected _subs?: Subscription; private readonly _datepicker: ComponentLoader; private _datepickerRef?: ComponentRef; @@ -92,28 +92,7 @@ export class BsDaterangepickerInlineDirective implements OnInit, OnDestroy, OnCh ngOnInit(): void { this.setConfig(); - - // if date changes from external source (model -> view) - this._subs.push( - this.bsValueChange.subscribe((value: Date[]) => { - if (this._datepickerRef) { - this._datepickerRef.instance.value = value; - } - }) - ); - - // if date changes from picker (view -> model) - if (this._datepickerRef) { - this._subs.push( - this._datepickerRef.instance.valueChange - .pipe( - filter((range: Date[]) => range && range[0] && !!range[1]) - ) - .subscribe((value: Date[]) => { - this.bsValue = value; - }) - ); - } + this.updateSubscriptions(); } ngOnChanges(changes: SimpleChanges): void { @@ -121,14 +100,16 @@ export class BsDaterangepickerInlineDirective implements OnInit, OnDestroy, OnCh return; } + let shouldSetConfig = false; + if (changes.minDate) { this._datepickerRef.instance.minDate = this.minDate; - this.setConfig(); + shouldSetConfig = true; } if (changes.maxDate) { this._datepickerRef.instance.maxDate = this.maxDate; - this.setConfig(); + shouldSetConfig = true; } if (changes.datesEnabled) { @@ -137,25 +118,56 @@ export class BsDaterangepickerInlineDirective implements OnInit, OnDestroy, OnCh if (changes.datesDisabled) { this._datepickerRef.instance.datesDisabled = this.datesDisabled; - this.setConfig(); + shouldSetConfig = true; } if (changes.daysDisabled) { this._datepickerRef.instance.daysDisabled = this.daysDisabled; - this.setConfig(); + shouldSetConfig = true; } if (changes.isDisabled) { this._datepickerRef.instance.isDisabled = this.isDisabled; - this.setConfig(); + shouldSetConfig = true; } if (changes.dateCustomClasses) { this._datepickerRef.instance.dateCustomClasses = this.dateCustomClasses; + shouldSetConfig = true; + } + + if (shouldSetConfig) { this.setConfig(); } } + updateSubscriptions(): void { + this._subs?.unsubscribe(); + this._subs = new Subscription(); + + // if date changes from external source (model -> view) + this._subs.add( + this.bsValueChange.subscribe((value: Date[]) => { + if (this._datepickerRef) { + this._datepickerRef.instance.value = value; + } + }) + ); + + // if date changes from picker (view -> model) + if (this._datepickerRef) { + this._subs.add( + this._datepickerRef.instance.valueChange + .pipe( + filter((range: Date[]) => range && range[0] && !!range[1]) + ) + .subscribe((value: Date[]) => { + this.bsValue = value; + }) + ); + } + } + /** * Set config for datepicker */ @@ -182,6 +194,8 @@ export class BsDaterangepickerInlineDirective implements OnInit, OnDestroy, OnCh .attach(BsDaterangepickerInlineContainerComponent) .to(this._elementRef) .show(); + + this.updateSubscriptions(); } ngOnDestroy() {