1
- import { isPlatformBrowser } from '@angular/common' ;
2
-
3
1
import {
4
2
AfterContentInit ,
5
3
AfterViewInit ,
@@ -22,12 +20,10 @@ import {
22
20
OnDestroy ,
23
21
OnInit ,
24
22
Output ,
25
- PLATFORM_ID ,
26
23
QueryList ,
27
24
Renderer2 ,
28
25
TrackByFunction ,
29
- ViewChild ,
30
- ViewContainerRef
26
+ ViewChild
31
27
} from '@angular/core' ;
32
28
import {
33
29
EMPTY ,
@@ -42,12 +38,14 @@ import {
42
38
timer
43
39
} from 'rxjs' ;
44
40
import { debounceTime , filter , map , startWith , switchMap , takeUntil } from 'rxjs/operators' ;
41
+
45
42
import {
46
43
NguCarouselDefDirective ,
47
44
NguCarouselNextDirective ,
48
45
NguCarouselOutlet ,
49
46
NguCarouselPrevDirective
50
47
} from './../ngu-carousel.directive' ;
48
+ import { IS_BROWSER } from '../symbols' ;
51
49
import {
52
50
Transfrom ,
53
51
Breakpoints ,
@@ -83,12 +81,7 @@ export class NguCarousel<T>
83
81
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
84
82
@Output ( ) onMove = new EventEmitter < NguCarousel < T > > ( ) ;
85
83
// isFirstss = 0;
86
- arrayChanges : IterableChanges < { } > ;
87
- carouselInt : Subscription ;
88
-
89
- listener1 : ( ) => void ;
90
- listener2 : ( ) => void ;
91
- listener3 : ( ) => void ;
84
+ private _arrayChanges : IterableChanges < { } > | null = null ;
92
85
93
86
@Input ( 'dataSource' )
94
87
get dataSource ( ) : any {
@@ -103,35 +96,27 @@ export class NguCarousel<T>
103
96
private _defaultNodeDef : NguCarouselDefDirective < any > | null ;
104
97
105
98
@ContentChildren ( NguCarouselDefDirective )
106
- private _defDirec : QueryList < NguCarouselDefDirective < any > > ;
99
+ private _defDirectives : QueryList < NguCarouselDefDirective < any > > ;
107
100
108
101
@ViewChild ( NguCarouselOutlet , { static : true } )
109
102
_nodeOutlet : NguCarouselOutlet ;
110
103
111
- /** The setter is used to catch the button if the button has ngIf
112
- * issue id #91
104
+ /**
105
+ * The setter is used to catch the button if the button is wrapped with `ngIf`.
106
+ * https://github.com/uiuniversal/ngu-carousel/issues/91
113
107
*/
114
- @ContentChild ( NguCarouselNextDirective , /* TODO: add static flag */ { read : ElementRef } )
115
- set nextBtn ( btn : ElementRef ) {
116
- this . listener2 ?.( ) ;
117
- if ( btn ) {
118
- this . listener2 = this . _renderer . listen ( btn . nativeElement , 'click' , ( ) =>
119
- this . _carouselScrollOne ( 1 )
120
- ) ;
121
- }
108
+ @ContentChild ( NguCarouselNextDirective , { read : ElementRef , static : false } )
109
+ set nextButton ( nextButton : ElementRef < HTMLElement > | undefined ) {
110
+ this . _nextButton$ . next ( nextButton ?. nativeElement ) ;
122
111
}
123
112
124
- /** The setter is used to catch the button if the button has ngIf
125
- * issue id #91
113
+ /**
114
+ * The setter is used to catch the button if the button is wrapped with `ngIf`.
115
+ * https://github.com/uiuniversal/ngu-carousel/issues/91
126
116
*/
127
- @ContentChild ( NguCarouselPrevDirective , /* TODO: add static flag */ { read : ElementRef } )
128
- set prevBtn ( btn : ElementRef ) {
129
- this . listener1 ?.( ) ;
130
- if ( btn ) {
131
- this . listener1 = this . _renderer . listen ( btn . nativeElement , 'click' , ( ) =>
132
- this . _carouselScrollOne ( 0 )
133
- ) ;
134
- }
117
+ @ContentChild ( NguCarouselPrevDirective , { read : ElementRef , static : false } )
118
+ set prevButton ( prevButton : ElementRef < HTMLElement > | undefined ) {
119
+ this . _prevButton$ . next ( prevButton ?. nativeElement ) ;
135
120
}
136
121
137
122
@ViewChild ( 'ngucarousel' , { read : ElementRef , static : true } )
@@ -141,7 +126,7 @@ export class NguCarousel<T>
141
126
private nguItemsContainer : ElementRef ;
142
127
143
128
@ViewChild ( 'touchContainer' , { read : ElementRef , static : true } )
144
- private touchContainer : ElementRef ;
129
+ private _touchContainer : ElementRef < HTMLElement > ;
145
130
146
131
private _intervalController$ = new Subject < number > ( ) ;
147
132
@@ -171,16 +156,21 @@ export class NguCarousel<T>
171
156
}
172
157
private _trackByFn : TrackByFunction < T > ;
173
158
159
+ /** Subjects used to notify whenever buttons are removed or rendered so we can re-add listeners. */
160
+ private readonly _prevButton$ = new Subject < HTMLElement | undefined > ( ) ;
161
+ private readonly _nextButton$ = new Subject < HTMLElement | undefined > ( ) ;
162
+
174
163
constructor (
175
164
private _el : ElementRef ,
176
165
private _renderer : Renderer2 ,
177
166
private _differs : IterableDiffers ,
178
- @Inject ( PLATFORM_ID ) private platformId : object ,
167
+ @Inject ( IS_BROWSER ) private _isBrowser : boolean ,
179
168
private _cdr : ChangeDetectorRef ,
180
169
private _ngZone : NgZone ,
181
170
private _nguWindowScrollListener : NguWindowScrollListener
182
171
) {
183
172
super ( ) ;
173
+ this . _setupButtonListeners ( ) ;
184
174
}
185
175
186
176
ngOnInit ( ) {
@@ -190,15 +180,15 @@ export class NguCarousel<T>
190
180
}
191
181
192
182
ngDoCheck ( ) {
193
- this . arrayChanges = this . _dataDiffer . diff ( this . dataSource ) ! ;
194
- if ( this . arrayChanges && this . _defDirec ) {
183
+ this . _arrayChanges = this . _dataDiffer . diff ( this . dataSource ) ! ;
184
+ if ( this . _arrayChanges && this . _defDirectives ) {
195
185
this . _observeRenderChanges ( ) ;
196
186
}
197
187
}
198
188
199
189
private _switchDataSource ( dataSource : any ) : any {
200
190
this . _dataSource = dataSource ;
201
- if ( this . _defDirec ) {
191
+ if ( this . _defDirectives ) {
202
192
this . _observeRenderChanges ( ) ;
203
193
}
204
194
}
@@ -222,13 +212,12 @@ export class NguCarousel<T>
222
212
}
223
213
}
224
214
225
- private renderNodeChanges (
226
- data : any [ ] ,
227
- viewContainer : ViewContainerRef = this . _nodeOutlet . viewContainer
228
- ) {
229
- if ( ! this . arrayChanges ) return ;
215
+ private renderNodeChanges ( data : any [ ] ) {
216
+ if ( ! this . _arrayChanges ) return ;
230
217
231
- this . arrayChanges . forEachOperation (
218
+ const viewContainer = this . _nodeOutlet . viewContainer ;
219
+
220
+ this . _arrayChanges . forEachOperation (
232
221
(
233
222
item : IterableChangeRecord < any > ,
234
223
adjustedPreviousIndex : number | null ,
@@ -274,12 +263,12 @@ export class NguCarousel<T>
274
263
}
275
264
276
265
private _getNodeDef ( data : any , i : number ) : NguCarouselDefDirective < any > {
277
- if ( this . _defDirec . length === 1 ) {
278
- return this . _defDirec . first ;
266
+ if ( this . _defDirectives . length === 1 ) {
267
+ return this . _defDirectives . first ;
279
268
}
280
269
281
270
const nodeDef : NguCarouselDefDirective < any > =
282
- this . _defDirec . find ( def => def . when && def . when ( i , data ) ) || this . _defaultNodeDef ! ;
271
+ this . _defDirectives . find ( def => ! ! def . when ?. ( i , data ) ) || this . _defaultNodeDef ! ;
283
272
284
273
return nodeDef ;
285
274
}
@@ -290,7 +279,7 @@ export class NguCarousel<T>
290
279
291
280
this . carouselCssNode = this . _createStyleElem ( ) ;
292
281
293
- if ( isPlatformBrowser ( this . platformId ) ) {
282
+ if ( this . _isBrowser ) {
294
283
this . _carouselInterval ( ) ;
295
284
if ( ! this . vertical . enabled && this . inputs . touch ) {
296
285
this . _setupHammer ( ) ;
@@ -338,17 +327,8 @@ export class NguCarousel<T>
338
327
ngOnDestroy ( ) {
339
328
this . _hammertime ?. destroy ( ) ;
340
329
this . _destroy$ . next ( ) ;
341
- this . carouselInt && this . carouselInt . unsubscribe ( ) ;
342
- this . _intervalController$ . unsubscribe ( ) ;
343
330
this . carouselLoad . complete ( ) ;
344
331
this . onMove . complete ( ) ;
345
-
346
- /** remove listeners */
347
- for ( let i = 1 ; i <= 3 ; i ++ ) {
348
- // TODO: revisit later.
349
- const str = `listener${ i } ` as 'listener1' | 'listener2' | 'listener3' ;
350
- this [ str ] && this [ str ] ( ) ;
351
- }
352
332
}
353
333
354
334
/** Get Touch input */
@@ -359,7 +339,7 @@ export class NguCarousel<T>
359
339
// the HammerJS is loaded.
360
340
. pipe ( takeUntil ( this . _destroy$ ) )
361
341
. subscribe ( ( ) => {
362
- const hammertime = ( this . _hammertime = new Hammer ( this . touchContainer . nativeElement ) ) ;
342
+ const hammertime = ( this . _hammertime = new Hammer ( this . _touchContainer . nativeElement ) ) ;
363
343
hammertime . get ( 'pan' ) . set ( { direction : Hammer . DIRECTION_HORIZONTAL } ) ;
364
344
365
345
hammertime . on ( 'panstart' , ( ev : any ) => {
@@ -471,7 +451,7 @@ export class NguCarousel<T>
471
451
/** store data based on width of the screen for the carousel */
472
452
private _storeCarouselData ( ) : void {
473
453
const breakpoints = this . inputs . gridBreakpoints ;
474
- this . deviceWidth = isPlatformBrowser ( this . platformId ) ? window . innerWidth : breakpoints ?. xl ! ;
454
+ this . deviceWidth = this . _isBrowser ? window . innerWidth : breakpoints ?. xl ! ;
475
455
476
456
this . carouselWidth = this . carouselMain1 . nativeElement . offsetWidth ;
477
457
@@ -831,20 +811,31 @@ export class NguCarousel<T>
831
811
832
812
const interval$ = interval ( this . inputs . interval ?. timing ! ) . pipe ( mapToOne ) ;
833
813
834
- setTimeout ( ( ) => {
835
- this . carouselInt = merge ( play$ , touchPlay$ , pause$ , touchPause$ , this . _intervalController$ )
836
- . pipe (
837
- startWith ( 1 ) ,
838
- switchMap ( val => {
839
- this . isHovered = ! val ;
840
- this . _cdr . markForCheck ( ) ;
841
- return val ? interval$ : EMPTY ;
842
- } )
843
- )
844
- . subscribe ( ( ) => {
845
- this . _carouselScrollOne ( 1 ) ;
846
- } ) ;
847
- } , this . interval . initialDelay ) ;
814
+ const initialDelay = this . interval . initialDelay || 0 ;
815
+
816
+ const carouselInterval$ = merge (
817
+ play$ ,
818
+ touchPlay$ ,
819
+ pause$ ,
820
+ touchPause$ ,
821
+ this . _intervalController$
822
+ ) . pipe (
823
+ startWith ( 1 ) ,
824
+ switchMap ( val => {
825
+ this . isHovered = ! val ;
826
+ this . _cdr . markForCheck ( ) ;
827
+ return val ? interval$ : EMPTY ;
828
+ } )
829
+ ) ;
830
+
831
+ timer ( initialDelay )
832
+ . pipe (
833
+ switchMap ( ( ) => carouselInterval$ ) ,
834
+ takeUntil ( this . _destroy$ )
835
+ )
836
+ . subscribe ( ( ) => {
837
+ this . _carouselScrollOne ( 1 ) ;
838
+ } ) ;
848
839
}
849
840
}
850
841
@@ -854,39 +845,41 @@ export class NguCarousel<T>
854
845
start : number ,
855
846
end : number ,
856
847
speed : number ,
857
- length : number ,
858
- viewContainer = this . _nodeOutlet . viewContainer
848
+ length : number
859
849
) : void {
850
+ const viewContainer = this . _nodeOutlet . viewContainer ;
851
+
860
852
let val = length < 5 ? length : 5 ;
861
853
val = val === 1 ? 3 : val ;
862
- const collectIndex : number [ ] = [ ] ;
854
+ const collectedIndexes : number [ ] = [ ] ;
863
855
864
856
if ( direction === 1 ) {
865
857
for ( let i = start - 1 ; i < end ; i ++ ) {
866
- collectIndex . push ( i ) ;
858
+ collectedIndexes . push ( i ) ;
867
859
val = val * 2 ;
868
860
const viewRef = viewContainer . get ( i ) as any ;
869
861
const context = viewRef . context as any ;
870
862
context . animate = { value : true , params : { distance : val } } ;
871
863
}
872
864
} else {
873
865
for ( let i = end - 1 ; i >= start - 1 ; i -- ) {
874
- collectIndex . push ( i ) ;
866
+ collectedIndexes . push ( i ) ;
875
867
val = val * 2 ;
876
868
const viewRef = viewContainer . get ( i ) as any ;
877
869
const context = viewRef . context as any ;
878
870
context . animate = { value : true , params : { distance : - val } } ;
879
871
}
880
872
}
881
873
this . _cdr . markForCheck ( ) ;
882
- setTimeout ( ( ) => {
883
- this . _removeAnimations ( collectIndex ) ;
884
- } , speed * 0.7 ) ;
874
+
875
+ timer ( speed * 0.7 )
876
+ . pipe ( takeUntil ( this . _destroy$ ) )
877
+ . subscribe ( ( ) => this . _removeAnimations ( collectedIndexes ) ) ;
885
878
}
886
879
887
- private _removeAnimations ( indexs : number [ ] ) {
880
+ private _removeAnimations ( collectedIndexes : number [ ] ) {
888
881
const viewContainer = this . _nodeOutlet . viewContainer ;
889
- indexs . forEach ( i => {
882
+ collectedIndexes . forEach ( i => {
890
883
const viewRef = viewContainer . get ( i ) as any ;
891
884
const context = viewRef . context as any ;
892
885
context . animate = { value : false , params : { distance : 0 } } ;
@@ -910,6 +903,23 @@ export class NguCarousel<T>
910
903
return styleItem ;
911
904
}
912
905
906
+ private _setupButtonListeners ( ) : void {
907
+ this . _prevButton$
908
+ . pipe (
909
+ // Returning `EMPTY` will remove event listener once the button is removed from the DOM.
910
+ switchMap ( prevButton => ( prevButton ? fromEvent ( prevButton , 'click' ) : EMPTY ) ) ,
911
+ takeUntil ( this . _destroy$ )
912
+ )
913
+ . subscribe ( ( ) => this . _carouselScrollOne ( 0 ) ) ;
914
+
915
+ this . _nextButton$
916
+ . pipe (
917
+ switchMap ( nextButton => ( nextButton ? fromEvent ( nextButton , 'click' ) : EMPTY ) ) ,
918
+ takeUntil ( this . _destroy$ )
919
+ )
920
+ . subscribe ( ( ) => this . _carouselScrollOne ( 1 ) ) ;
921
+ }
922
+
913
923
private _setupWindowResizeListener ( ) : void {
914
924
this . _ngZone . runOutsideAngular ( ( ) =>
915
925
fromEvent ( window , 'resize' )
0 commit comments