@@ -43,8 +43,10 @@ import {VIRTUAL_SCROLL_STRATEGY, VirtualScrollStrategy} from './virtual-scroll-s
43
43
preserveWhitespaces : false ,
44
44
} )
45
45
export class CdkVirtualScrollViewport implements OnInit , DoCheck , OnDestroy {
46
- private _disconnectSubject = new Subject < void > ( ) ;
46
+ /** Emits when the viewport is detached from a CdkVirtualForOf. */
47
+ private _detachedSubject = new Subject < void > ( ) ;
47
48
49
+ /** Emits when the rendered range changes. */
48
50
private _renderedRangeSubject = new Subject < Range > ( ) ;
49
51
50
52
/** The direction the viewport scrolls. */
@@ -53,73 +55,101 @@ export class CdkVirtualScrollViewport implements OnInit, DoCheck, OnDestroy {
53
55
/** The element that wraps the rendered content. */
54
56
@ViewChild ( 'contentWrapper' ) _contentWrapper : ElementRef ;
55
57
56
- /** The total size of all content, including content that is not currently rendered. */
57
- get totalContentSize ( ) { return this . _totalContentSize ; }
58
- set totalContentSize ( size : number ) {
58
+ /** A stream that emits whenever the rendered range changes. */
59
+ renderedRangeStream : Observable < Range > = this . _renderedRangeSubject . asObservable ( ) ;
60
+
61
+ /**
62
+ * The total size of all content (in pixels), including content that is not currently rendered.
63
+ */
64
+ _totalContentSize = 0 ;
65
+
66
+ /** The transform used to offset the rendered content wrapper element. */
67
+ _renderedContentTransform : string ;
68
+
69
+ /** The currently rendered range of indices. */
70
+ private _renderedRange : Range = { start : 0 , end : 0 } ;
71
+
72
+ /** The length of the data bound to this viewport (in number of items). */
73
+ private _dataLength = 0 ;
74
+
75
+ /** The size of the viewport (in pixels). */
76
+ private _viewportSize = 0 ;
77
+
78
+ /** Whether this viewport is attached to a CdkVirtualForOf. */
79
+ private _isAttached = false ;
80
+
81
+ /**
82
+ * The scroll handling status.
83
+ * needed - The scroll state needs to be updated, but a check hasn't yet been scheduled.
84
+ * pending - The scroll state needs to be updated, and an update has already been scheduled.
85
+ * done - The scroll state does not need to be updated.
86
+ */
87
+ private _scrollHandledStatus : 'needed' | 'pending' | 'done' = 'done' ;
88
+
89
+ constructor ( public elementRef : ElementRef , private _changeDetectorRef : ChangeDetectorRef ,
90
+ private _ngZone : NgZone ,
91
+ @Inject ( VIRTUAL_SCROLL_STRATEGY ) private _scrollStrategy : VirtualScrollStrategy ) { }
92
+
93
+ /** Gets the length of the data bound to this viewport (in number of items). */
94
+ getDataLength ( ) : number {
95
+ return this . _dataLength ;
96
+ }
97
+
98
+ /** Gets the size of the viewport (in pixels). */
99
+ getViewportSize ( ) : number {
100
+ return this . _viewportSize ;
101
+ }
102
+
103
+ // TODO(mmalebra): Consider calling `detectChanges()` directly rather than the methods below.
104
+
105
+ /**
106
+ * Sets the total size of all content (in pixels), including content that is not currently
107
+ * rendered.
108
+ */
109
+ setTotalContentSize ( size : number ) {
59
110
if ( this . _totalContentSize != size ) {
111
+ // Re-enter the Angular zone so we can mark for change detection.
60
112
this . _ngZone . run ( ( ) => {
61
113
this . _totalContentSize = size ;
62
114
this . _changeDetectorRef . markForCheck ( ) ;
63
115
} ) ;
64
116
}
65
117
}
66
- private _totalContentSize = 0 ;
67
118
68
- /** The currently rendered range of indices. */
69
- get renderedRange ( ) { return this . _renderedRange ; }
70
- set renderedRange ( range : Range ) {
119
+ /** Sets the currently rendered range of indices. */
120
+ setRenderedRange ( range : Range ) {
71
121
if ( ! this . _rangesEqual ( this . _renderedRange , range ) ) {
122
+ // Re-enter the Angular zone so we can mark for change detection.
72
123
this . _ngZone . run ( ( ) => {
73
124
this . _renderedRangeSubject . next ( this . _renderedRange = range ) ;
74
125
this . _changeDetectorRef . markForCheck ( ) ;
75
126
} ) ;
76
127
}
77
128
}
78
- private _renderedRange : Range = { start : 0 , end : 0 } ;
79
129
80
- /** The offset of the rendered portion of the data from the start. */
81
- get renderedContentOffset ( ) : number { return this . _renderedContentOffset ; }
82
- set renderedContentOffset ( offset : number ) {
83
- if ( this . _renderedContentOffset != offset ) {
130
+ /** Sets the offset of the rendered portion of the data from the start (in pixels). */
131
+ setRenderedContentOffset ( offset : number ) {
132
+ const transform =
133
+ this . orientation === 'horizontal' ? `translateX(${ offset } px)` : `translateY(${ offset } px)` ;
134
+ if ( this . _renderedContentTransform != transform ) {
135
+ // Re-enter the Angular zone so we can mark for change detection.
84
136
this . _ngZone . run ( ( ) => {
85
- this . _renderedContentOffset = offset ;
86
- this . _renderedContentTransform = this . orientation === 'horizontal' ?
87
- `translateX(${ offset } px)` : `translateY(${ offset } px)` ;
137
+ this . _renderedContentTransform = transform ;
88
138
this . _changeDetectorRef . markForCheck ( ) ;
89
139
} ) ;
90
140
}
91
141
}
92
- private _renderedContentOffset = 0 ;
93
-
94
- /** The length of the data connected to this viewport. */
95
- get dataLength ( ) { return this . _dataLength ; }
96
- private _dataLength = 0 ;
97
-
98
- /** The size of the viewport. */
99
- get viewportSize ( ) { return this . _viewportSize ; }
100
- private _viewportSize = 0 ;
101
-
102
- /** A stream that emits whenever the rendered range changes. */
103
- renderedRangeStream : Observable < Range > = this . _renderedRangeSubject . asObservable ( ) ;
104
-
105
- _renderedContentTransform : string ;
106
-
107
- private _connected = false ;
108
-
109
- private _scrollHandledStatus : 'needed' | 'pending' | 'done' = 'done' ;
110
-
111
- constructor ( public elementRef : ElementRef , private _changeDetectorRef : ChangeDetectorRef ,
112
- private _ngZone : NgZone ,
113
- @Inject ( VIRTUAL_SCROLL_STRATEGY ) private _scrollStrategy : VirtualScrollStrategy ) { }
114
142
115
- /** Connect a `CdkVirtualForOf` to this viewport. */
116
- connect ( forOf : CdkVirtualForOf < any > ) {
117
- if ( this . _connected ) {
118
- throw Error ( 'CdkVirtualScrollViewport is already connected .' ) ;
143
+ /** Attaches a `CdkVirtualForOf` to this viewport. */
144
+ attach ( forOf : CdkVirtualForOf < any > ) {
145
+ if ( this . _isAttached ) {
146
+ throw Error ( 'CdkVirtualScrollViewport is already attached .' ) ;
119
147
}
120
148
121
- this . _connected = true ;
122
- forOf . dataStream . pipe ( takeUntil ( this . _disconnectSubject ) ) . subscribe ( data => {
149
+ this . _isAttached = true ;
150
+ // Subscribe to the data stream of the CdkVirtualForOf to keep track of when the data length
151
+ // changes.
152
+ forOf . dataStream . pipe ( takeUntil ( this . _detachedSubject ) ) . subscribe ( data => {
123
153
const len = data . length ;
124
154
if ( len != this . _dataLength ) {
125
155
this . _dataLength = len ;
@@ -128,13 +158,13 @@ export class CdkVirtualScrollViewport implements OnInit, DoCheck, OnDestroy {
128
158
} ) ;
129
159
}
130
160
131
- /** Disconnect the current `CdkVirtualForOf`. */
132
- disconnect ( ) {
133
- this . _connected = false ;
134
- this . _disconnectSubject . next ( ) ;
161
+ /** Detaches the current `CdkVirtualForOf`. */
162
+ detach ( ) {
163
+ this . _isAttached = false ;
164
+ this . _detachedSubject . next ( ) ;
135
165
}
136
166
137
- /** Gets the current scroll offset of the viewport. */
167
+ /** Gets the current scroll offset of the viewport (in pixels) . */
138
168
measureScrollOffset ( ) {
139
169
return this . orientation === 'horizontal' ?
140
170
this . elementRef . nativeElement . scrollLeft : this . elementRef . nativeElement . scrollTop ;
@@ -164,23 +194,26 @@ export class CdkVirtualScrollViewport implements OnInit, DoCheck, OnDestroy {
164
194
}
165
195
166
196
ngOnDestroy ( ) {
167
- this . disconnect ( ) ;
197
+ this . detach ( ) ;
168
198
this . _scrollStrategy . detach ( ) ;
169
199
170
200
// Complete all subjects
171
- this . _disconnectSubject . complete ( ) ;
201
+ this . _detachedSubject . complete ( ) ;
172
202
this . _renderedRangeSubject . complete ( ) ;
173
203
}
174
204
205
+ /** Marks that a scroll event happened and that the scroll state should be checked. */
175
206
private _markScrolled ( ) {
176
207
if ( this . _scrollHandledStatus === 'done' ) {
208
+ // Re-enter the Angular zone so we can mark for change detection.
177
209
this . _ngZone . run ( ( ) => {
178
210
this . _scrollHandledStatus = 'needed' ;
179
211
this . _changeDetectorRef . markForCheck ( ) ;
180
212
} ) ;
181
213
}
182
214
}
183
215
216
+ /** Checks if the given ranges are equal. */
184
217
private _rangesEqual ( r1 : Range , r2 : Range ) : boolean {
185
218
return r1 . start == r2 . start && r1 . end == r2 . end ;
186
219
}
0 commit comments