6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
8
9
- import { ArrayDataSource , CollectionViewer , DataSource , Range } from '@angular/cdk/collections' ;
9
+ import { CollectionViewer , DataSource , Range , StaticArrayDataSource } from '@angular/cdk/collections' ;
10
10
import {
11
11
Directive ,
12
12
DoCheck ,
@@ -32,24 +32,22 @@ import {Subject} from 'rxjs/Subject';
32
32
import { CdkVirtualScrollViewport } from './virtual-scroll-viewport' ;
33
33
34
34
35
- /** The context for an item rendered by `CdkForOf` */
36
- export class CdkForOfContext < T > {
37
- constructor ( public $implicit : T , public cdkForOf : NgIterable < T > | DataSource < T > ,
38
- public index : number , public count : number ) { }
39
-
40
- get first ( ) : boolean { return this . index === 0 ; }
41
-
42
- get last ( ) : boolean { return this . index === this . count - 1 ; }
43
-
44
- get even ( ) : boolean { return this . index % 2 === 0 ; }
45
-
46
- get odd ( ) : boolean { return ! this . even ; }
47
- }
35
+ /** The context for an item rendered by `CdkVirtualForOf` */
36
+ export type CdkVirtualForOfContext < T > = {
37
+ $implicit : T ;
38
+ cdkVirtualForOf : NgIterable < T > | DataSource < T > ;
39
+ index : number ;
40
+ count : number ;
41
+ first : boolean ;
42
+ last : boolean ;
43
+ even : boolean ;
44
+ odd : boolean ;
45
+ } ;
48
46
49
47
50
48
type RecordViewTuple < T > = {
51
49
record : IterableChangeRecord < T > | null ,
52
- view ?: EmbeddedViewRef < CdkForOfContext < T > >
50
+ view ?: EmbeddedViewRef < CdkVirtualForOfContext < T > >
53
51
} ;
54
52
55
53
@@ -58,53 +56,63 @@ type RecordViewTuple<T> = {
58
56
* container.
59
57
*/
60
58
@Directive ( {
61
- selector : '[cdkFor][cdkForOf ]' ,
59
+ selector : '[cdkVirtualFor][cdkVirtualForOf ]' ,
62
60
} )
63
- export class CdkForOf < T > implements CollectionViewer , DoCheck , OnDestroy {
61
+ export class CdkVirtualForOf < T > implements CollectionViewer , DoCheck , OnDestroy {
64
62
/** Emits when the rendered view of the data changes. */
65
63
viewChange = new Subject < Range > ( ) ;
66
64
67
- /** Emits when the data source changes . */
68
- private _dataSourceSubject = new Subject < DataSource < T > > ( ) ;
65
+ /** Subject that emits when a new DataSource instance is given . */
66
+ private _dataSourceChanges = new Subject < DataSource < T > > ( ) ;
69
67
70
68
/** The DataSource to display. */
71
69
@Input ( )
72
- get cdkForOf ( ) : NgIterable < T > | DataSource < T > { return this . _cdkForOf ; }
73
- set cdkForOf ( value : NgIterable < T > | DataSource < T > ) {
74
- this . _cdkForOf = value ;
75
- let ds = value instanceof DataSource ? value :
76
- new ArrayDataSource < T > ( Array . prototype . slice . call ( value ) ) ;
77
- this . _dataSourceSubject . next ( ds ) ;
70
+ get cdkVirtualForOf ( ) : NgIterable < T > | DataSource < T > { return this . _cdkVirtualForOf ; }
71
+ set cdkVirtualForOf ( value : NgIterable < T > | DataSource < T > ) {
72
+ this . _cdkVirtualForOf = value ;
73
+ const ds = value instanceof DataSource ? value :
74
+ // Slice the value since NgIterable may be array-like rather than an array.
75
+ new StaticArrayDataSource < T > ( Array . prototype . slice . call ( value ) ) ;
76
+ this . _dataSourceChanges . next ( ds ) ;
78
77
}
79
- _cdkForOf : NgIterable < T > | DataSource < T > ;
78
+ _cdkVirtualForOf : NgIterable < T > | DataSource < T > ;
80
79
81
- /** The trackBy function to use for tracking elements. */
80
+ /**
81
+ * The `TrackByFunction` to use for tracking changes. The `TrackByFunction` takes the index and
82
+ * the item and produces a value to be used as the item's identity when tracking changes.
83
+ */
82
84
@Input ( )
83
- get cdkForTrackBy ( ) : TrackByFunction < T > {
84
- return this . _cdkForOfTrackBy ;
85
+ get cdkVirtualForTrackBy ( ) : TrackByFunction < T > {
86
+ return this . _cdkVirtualForTrackBy ;
85
87
}
86
- set cdkForTrackBy ( fn : TrackByFunction < T > ) {
88
+ set cdkVirtualForTrackBy ( fn : TrackByFunction < T > ) {
87
89
this . _needsUpdate = true ;
88
- this . _cdkForOfTrackBy =
90
+ this . _cdkVirtualForTrackBy =
89
91
( index , item ) => fn ( index + ( this . _renderedRange ? this . _renderedRange . start : 0 ) , item ) ;
90
92
}
91
- private _cdkForOfTrackBy : TrackByFunction < T > ;
93
+ private _cdkVirtualForTrackBy : TrackByFunction < T > ;
92
94
93
95
/** The template used to stamp out new elements. */
94
96
@Input ( )
95
- set cdkForTemplate ( value : TemplateRef < CdkForOfContext < T > > ) {
97
+ set cdkVirtualForTemplate ( value : TemplateRef < CdkVirtualForOfContext < T > > ) {
96
98
if ( value ) {
97
99
this . _needsUpdate = true ;
98
100
this . _template = value ;
99
101
}
100
102
}
101
103
102
104
/** Emits whenever the data in the current DataSource changes. */
103
- dataStream : Observable < T [ ] > = this . _dataSourceSubject
105
+ dataStream : Observable < T [ ] > = this . _dataSourceChanges
104
106
. pipe (
107
+ // Start off with null `DataSource`.
105
108
startWith ( null ! ) ,
109
+ // Bundle up the previous and current data sources so we can work with both.
106
110
pairwise ( ) ,
111
+ // Use `_changeDataSource` to disconnect from the previous data source and connect to the
112
+ // new one, passing back a stream of data changes which we run through `switchMap` to give
113
+ // us a data stream that emits the latest data from whatever the current `DataSource` is.
107
114
switchMap ( ( [ prev , cur ] ) => this . _changeDataSource ( prev , cur ) ) ,
115
+ // Replay the last emitted data when someone subscribes.
108
116
shareReplay ( 1 ) ) ;
109
117
110
118
private _differ : IterableDiffer < T > | null = null ;
@@ -115,13 +123,13 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
115
123
116
124
private _renderedRange : Range ;
117
125
118
- private _templateCache : EmbeddedViewRef < CdkForOfContext < T > > [ ] = [ ] ;
126
+ private _templateCache : EmbeddedViewRef < CdkVirtualForOfContext < T > > [ ] = [ ] ;
119
127
120
128
private _needsUpdate = false ;
121
129
122
130
constructor (
123
131
private _viewContainerRef : ViewContainerRef ,
124
- private _template : TemplateRef < CdkForOfContext < T > > ,
132
+ private _template : TemplateRef < CdkVirtualForOfContext < T > > ,
125
133
private _differs : IterableDiffers ,
126
134
@Host ( ) private _viewport : CdkVirtualScrollViewport ) {
127
135
this . dataStream . subscribe ( data => this . _data = data ) ;
@@ -141,7 +149,8 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
141
149
throw Error ( 'Error: attempted to measure an element that isn\'t rendered.' ) ;
142
150
}
143
151
index -= this . _renderedRange . start ;
144
- let view = this . _viewContainerRef . get ( index ) as EmbeddedViewRef < CdkForOfContext < T > > | null ;
152
+ let view = this . _viewContainerRef . get ( index ) as
153
+ EmbeddedViewRef < CdkVirtualForOfContext < T > > | null ;
145
154
if ( view && view . rootNodes . length ) {
146
155
let minTop = Infinity ;
147
156
let minLeft = Infinity ;
@@ -172,15 +181,19 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
172
181
ngDoCheck ( ) {
173
182
if ( this . _differ && this . _needsUpdate ) {
174
183
const changes = this . _differ . diff ( this . _renderedItems ) ;
175
- this . _applyChanges ( changes ) ;
184
+ if ( ! changes ) {
185
+ this . _updateContext ( ) ;
186
+ } else {
187
+ this . _applyChanges ( changes ) ;
188
+ }
176
189
this . _needsUpdate = false ;
177
190
}
178
191
}
179
192
180
193
ngOnDestroy ( ) {
181
194
this . _viewport . disconnect ( ) ;
182
195
183
- this . _dataSourceSubject . complete ( ) ;
196
+ this . _dataSourceChanges . complete ( ) ;
184
197
this . viewChange . complete ( ) ;
185
198
186
199
for ( let view of this . _templateCache ) {
@@ -194,7 +207,7 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
194
207
this . viewChange . next ( this . _renderedRange ) ;
195
208
this . _renderedItems = this . _data . slice ( this . _renderedRange . start , this . _renderedRange . end ) ;
196
209
if ( ! this . _differ ) {
197
- this . _differ = this . _differs . find ( this . _renderedItems ) . create ( this . cdkForTrackBy ) ;
210
+ this . _differ = this . _differs . find ( this . _renderedItems ) . create ( this . cdkVirtualForTrackBy ) ;
198
211
}
199
212
this . _needsUpdate = true ;
200
213
}
@@ -208,24 +221,24 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
208
221
return newDs . connect ( this ) ;
209
222
}
210
223
211
- /** Apply changes to the DOM. */
212
- private _applyChanges ( changes : IterableChanges < T > | null ) {
213
- // If there are no changes, just update the index and count on the view context and be done.
214
- if ( ! changes ) {
215
- for ( let i = 0 , len = this . _viewContainerRef . length ; i < len ; i ++ ) {
216
- let view = this . _viewContainerRef . get ( i ) as EmbeddedViewRef < CdkForOfContext < T > > ;
217
- view . context . index = this . _renderedRange . start + i ;
218
- view . context . count = this . _data . length ;
219
- view . detectChanges ( ) ;
220
- }
221
- return ;
224
+ /** Update the `CdkVirtualForOfContext` for all views. */
225
+ private _updateContext ( ) {
226
+ for ( let i = 0 , len = this . _viewContainerRef . length ; i < len ; i ++ ) {
227
+ let view = this . _viewContainerRef . get ( i ) as EmbeddedViewRef < CdkVirtualForOfContext < T > > ;
228
+ view . context . index = this . _renderedRange . start + i ;
229
+ view . context . count = this . _data . length ;
230
+ this . _updateComputedContextProperties ( view . context ) ;
231
+ view . detectChanges ( ) ;
222
232
}
233
+ }
223
234
235
+ /** Apply changes to the DOM. */
236
+ private _applyChanges ( changes : IterableChanges < T > ) {
224
237
// Detach all of the views and add them into an array to preserve their original order.
225
- const previousViews : EmbeddedViewRef < CdkForOfContext < T > > [ ] = [ ] ;
238
+ const previousViews : EmbeddedViewRef < CdkVirtualForOfContext < T > > [ ] = [ ] ;
226
239
for ( let i = 0 , len = this . _viewContainerRef . length ; i < len ; i ++ ) {
227
240
previousViews . unshift (
228
- this . _viewContainerRef . detach ( ) ! as EmbeddedViewRef < CdkForOfContext < T > > ) ;
241
+ this . _viewContainerRef . detach ( ) ! as EmbeddedViewRef < CdkVirtualForOfContext < T > > ) ;
229
242
}
230
243
231
244
// Mark the removed indices so we can recycle their views.
@@ -255,21 +268,43 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
255
268
}
256
269
257
270
// We now have a full list of everything to be inserted, so go ahead and insert them.
271
+ this . _insertViews ( insertTuples ) ;
272
+ }
273
+
274
+ /** Insert the RecordViewTuples into the container element. */
275
+ private _insertViews ( insertTuples : RecordViewTuple < T > [ ] ) {
258
276
for ( let i = 0 , len = insertTuples . length ; i < len ; i ++ ) {
259
277
let { view, record} = insertTuples [ i ] ;
260
278
if ( view ) {
261
279
this . _viewContainerRef . insert ( view ) ;
262
280
} else {
263
- view = this . _viewContainerRef . createEmbeddedView ( this . _template ,
264
- new CdkForOfContext < T > ( null ! , this . _cdkForOf , - 1 , - 1 ) ) ;
281
+ view = this . _viewContainerRef . createEmbeddedView ( this . _template , {
282
+ $implicit : null ! ,
283
+ cdkVirtualForOf : this . _cdkVirtualForOf ,
284
+ index : - 1 ,
285
+ count : - 1 ,
286
+ first : false ,
287
+ last : false ,
288
+ odd : false ,
289
+ even : false
290
+ } ) ;
265
291
}
266
292
267
293
if ( record ) {
268
294
view . context . $implicit = record . item as T ;
269
295
}
270
296
view . context . index = this . _renderedRange . start + i ;
271
297
view . context . count = this . _data . length ;
298
+ this . _updateComputedContextProperties ( view . context ) ;
272
299
view . detectChanges ( ) ;
273
300
}
274
301
}
302
+
303
+ /** Update the computed properties on the `CdkVirtualForOfContext`. */
304
+ private _updateComputedContextProperties ( context : CdkVirtualForOfContext < any > ) {
305
+ context . first = context . index === 0 ;
306
+ context . last = context . index === context . count - 1 ;
307
+ context . even = context . index % 2 === 0 ;
308
+ context . odd = ! context . even ;
309
+ }
275
310
}
0 commit comments