Skip to content

Commit 044a294

Browse files
authored
fix: check length only on unwrapped data (#442)
1 parent fc0f985 commit 044a294

File tree

1 file changed

+38
-20
lines changed

1 file changed

+38
-20
lines changed

libs/ngu/carousel/src/lib/ngu-carousel/ngu-carousel.component.ts

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
EventEmitter,
1212
Inject,
1313
Input,
14-
isDevMode,
1514
IterableChangeRecord,
1615
IterableChanges,
1716
IterableDiffer,
@@ -57,6 +56,14 @@ import { NguWindowScrollListener } from './ngu-window-scroll-listener';
5756

5857
type DirectionSymbol = '' | '-';
5958

59+
type NguCarouselDataSource = Observable<any[]> | any[] | null | undefined;
60+
61+
// This will be provided through Terser global definitions by Angular CLI.
62+
// This is how Angular does tree-shaking internally.
63+
declare const ngDevMode: boolean;
64+
65+
const NG_DEV_MODE = typeof ngDevMode === 'undefined' || ngDevMode;
66+
6067
@Component({
6168
selector: 'ngu-carousel',
6269
templateUrl: 'ngu-carousel.component.html',
@@ -81,16 +88,26 @@ export class NguCarousel<T>
8188

8289
private _arrayChanges: IterableChanges<{}> | null = null;
8390

84-
@Input('dataSource')
85-
get dataSource(): any {
91+
@Input()
92+
get dataSource(): NguCarouselDataSource {
8693
return this._dataSource;
8794
}
88-
set dataSource(data: any) {
95+
set dataSource(data: NguCarouselDataSource) {
8996
if (data) {
9097
this._switchDataSource(data);
9198
}
9299
}
93-
private _dataSource: any;
100+
private _dataSource: NguCarouselDataSource = null;
101+
102+
/**
103+
* `_dataSource` allows multiple values to be set considering nullable and
104+
* observable values. We shouldn't try to get `_dataSource.length` since it
105+
* might be `null|undefined` which will throw an error that property doesn't
106+
* exist on `undefined`. It will also always equal `undefined` on observable.
107+
* We should wait until the observable is unwrapped and then check the length
108+
* of the actual unwrapped data.
109+
*/
110+
private _unwrappedData: any[] = [];
94111

95112
private _defaultNodeDef: NguCarouselDefDirective<any> | null;
96113

@@ -156,7 +173,7 @@ export class NguCarousel<T>
156173
return this._trackByFn;
157174
}
158175
set trackBy(fn: TrackByFunction<T>) {
159-
if (isDevMode() && fn != null && typeof fn !== 'function' && console && console.warn) {
176+
if (NG_DEV_MODE && fn != null && typeof fn !== 'function' && console?.warn) {
160177
console.warn(`trackBy must be a function, but received ${JSON.stringify(fn)}.`);
161178
}
162179
this._trackByFn = fn;
@@ -181,13 +198,13 @@ export class NguCarousel<T>
181198
}
182199

183200
ngOnInit() {
184-
this._dataDiffer = this._differs.find([]).create((_i: number, item: any) => {
185-
return this.trackBy ? this.trackBy(_i, item) : item;
186-
});
201+
this._dataDiffer = this._differs
202+
.find([])
203+
.create((index: number, item: any) => (this.trackBy ? this.trackBy(index, item) : item));
187204
}
188205

189206
ngDoCheck() {
190-
this._arrayChanges = this._dataDiffer.diff(this.dataSource)!;
207+
this._arrayChanges = this._dataDiffer.diff(this._unwrappedData)!;
191208
if (this._arrayChanges && this._defDirectives) {
192209
this._observeRenderChanges();
193210
}
@@ -210,8 +227,9 @@ export class NguCarousel<T>
210227
}
211228

212229
dataStream
213-
?.pipe(takeUntil(this._intervalController$), takeUntil(this._destroy$))
230+
?.pipe(takeUntil(merge(this._intervalController$, this._destroy$)))
214231
.subscribe(data => {
232+
this._unwrappedData = data;
215233
this.renderNodeChanges(data);
216234
this.isLast = this._pointIndex === this.currentSlide;
217235
});
@@ -331,8 +349,6 @@ export class NguCarousel<T>
331349
ngOnDestroy() {
332350
this._hammertime?.destroy();
333351
this._destroy$.next();
334-
this.carouselLoad.complete();
335-
this.onMove.complete();
336352
}
337353

338354
/** Get Touch input */
@@ -495,7 +511,7 @@ export class NguCarousel<T>
495511

496512
/** Init carousel point */
497513
private _carouselPoint(): void {
498-
const Nos = this.dataSource.length - (this.items - this.slideItems);
514+
const Nos = this._unwrappedData.length - (this.items - this.slideItems);
499515
this._pointIndex = Math.ceil(Nos / this.slideItems);
500516
const pointers: number[] = [];
501517

@@ -540,7 +556,7 @@ export class NguCarousel<T>
540556
break;
541557
case this._pointIndex - 1:
542558
this._btnBoolean(0, 1);
543-
slideremains = this.dataSource.length - this.items;
559+
slideremains = this._unwrappedData.length - this.items;
544560
break;
545561
default:
546562
this._btnBoolean(0, 0);
@@ -653,7 +669,7 @@ export class NguCarousel<T>
653669
const MoveSlide = currentSlideD + this.slideItems;
654670
this._btnBoolean(0, 1);
655671
if (this.currentSlide === 0) {
656-
currentSlide = this.dataSource.length - this.items;
672+
currentSlide = this._unwrappedData.length - this.items;
657673
itemSpeed = 400;
658674
this._btnBoolean(0, 1);
659675
} else if (this.slideItems >= MoveSlide) {
@@ -671,10 +687,10 @@ export class NguCarousel<T>
671687
this._carouselScrollTwo(Btn, currentSlide, itemSpeed);
672688
} else if (Btn === 1 && ((!this.loop && !this.isLast) || this.loop)) {
673689
if (
674-
this.dataSource.length <= this.currentSlide + this.items + this.slideItems &&
690+
this._unwrappedData.length <= this.currentSlide + this.items + this.slideItems &&
675691
!this.isLast
676692
) {
677-
currentSlide = this.dataSource.length - this.items;
693+
currentSlide = this._unwrappedData.length - this.items;
678694
this._btnBoolean(0, 1);
679695
} else if (this.isLast) {
680696
currentSlide = 0;
@@ -720,7 +736,7 @@ export class NguCarousel<T>
720736
this._setStyle(this._nguItemsContainer.nativeElement, 'transition', ``);
721737
}
722738

723-
this.itemLength = this.dataSource.length;
739+
this.itemLength = this._unwrappedData.length;
724740
this._transformStyle(currentSlide);
725741
this.currentSlide = currentSlide;
726742
this.onMove.emit(this);
@@ -773,7 +789,7 @@ export class NguCarousel<T>
773789
/** this will trigger the carousel to load the items */
774790
private _carouselLoadTrigger(): void {
775791
if (typeof this.inputs.load === 'number') {
776-
this.dataSource.length - this.load <= this.currentSlide + this.items &&
792+
this._unwrappedData.length - this.load <= this.currentSlide + this.items &&
777793
this.carouselLoad.emit(this.currentSlide);
778794
}
779795
}
@@ -944,4 +960,6 @@ export class NguCarousel<T>
944960
})
945961
);
946962
}
963+
964+
static ngAcceptInputType_dataSource: NguCarouselDataSource;
947965
}

0 commit comments

Comments
 (0)