Skip to content

Commit 138e2be

Browse files
committed
address comments
1 parent 38afb9e commit 138e2be

File tree

2 files changed

+33
-25
lines changed

2 files changed

+33
-25
lines changed

src/cdk-experimental/scrolling/virtual-scroll-viewport.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {animationFrameScheduler, Subject} from 'rxjs';
66
import {CdkVirtualScrollViewport, CdkVirtualForOf, ScrollingModule} from './index';
77

88

9-
fdescribe('CdkVirtualScrollViewport', () => {
9+
describe('CdkVirtualScrollViewport', () => {
1010
describe ('with FixedSizeVirtualScrollStrategy', () => {
1111
let fixture: ComponentFixture<FixedSizeVirtualScroll>;
1212
let testComponent: FixedSizeVirtualScroll;

src/cdk-experimental/scrolling/virtual-scroll-viewport.ts

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,20 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
102102
private _destroyed = new Subject<void>();
103103

104104
/** Whether there is a pending change detection cycle. */
105-
private _checkPending = false;
105+
private _isChangeDetectionPending = false;
106106

107107
/** A list of functions to run after the next change detection cycle. */
108-
private _runAfterCheck: Function[] = [];
108+
private _runAfterChangeDetection: Function[] = [];
109109

110110
constructor(public elementRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef,
111111
private _ngZone: NgZone, private _sanitizer: DomSanitizer,
112112
@Inject(VIRTUAL_SCROLL_STRATEGY) private _scrollStrategy: VirtualScrollStrategy) {}
113113

114114
ngOnInit() {
115115
// It's still too early to measure the viewport at this point. Deferring with a promise allows
116-
// the Viewport to be rendered with the correct size before we measure.
116+
// the Viewport to be rendered with the correct size before we measure. We run this outside the
117+
// zone to avoid causing more change detection cycles. We handle the change detection loop
118+
// ourselves instead.
117119
this._ngZone.runOutsideAngular(() => Promise.resolve().then(() => {
118120
this._measureViewportSize();
119121
this._scrollStrategy.attach(this);
@@ -124,7 +126,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
124126
.pipe(sampleTime(0, animationFrameScheduler), takeUntil(this._destroyed))
125127
.subscribe(() => this._scrollStrategy.onContentScrolled());
126128

127-
this._markForCheck();
129+
this._markChangeDetectionNeeded();
128130
}));
129131
}
130132

@@ -146,13 +148,14 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
146148
}
147149

148150
// Subscribe to the data stream of the CdkVirtualForOf to keep track of when the data length
149-
// changes.
151+
// changes. Run outside the zone to avoid triggering change detection, since we're managing the
152+
// change detection loop ourselves.
150153
this._ngZone.runOutsideAngular(() => {
151154
this._forOf = forOf;
152155
this._forOf.dataStream.pipe(takeUntil(this._detachedSubject)).subscribe(data => {
153-
const len = data.length;
154-
if (len !== this._dataLength) {
155-
this._dataLength = len;
156+
const newLength = data.length;
157+
if (newLength !== this._dataLength) {
158+
this._dataLength = newLength;
156159
this._scrollStrategy.onDataLengthChanged();
157160
}
158161
});
@@ -192,15 +195,15 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
192195
setTotalContentSize(size: number) {
193196
if (this._totalContentSize !== size) {
194197
this._totalContentSize = size;
195-
this._markForCheck();
198+
this._markChangeDetectionNeeded();
196199
}
197200
}
198201

199202
/** Sets the currently rendered range of indices. */
200203
setRenderedRange(range: ListRange) {
201204
if (!rangesEqual(this._renderedRange, range)) {
202205
this._renderedRangeSubject.next(this._renderedRange = range);
203-
this._markForCheck(() => this._scrollStrategy.onContentRendered());
206+
this._markChangeDetectionNeeded(() => this._scrollStrategy.onContentRendered());
204207
}
205208
}
206209

@@ -231,7 +234,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
231234
// into the string.
232235
this._rawRenderedContentTransform = transform;
233236
this._renderedContentTransform = this._sanitizer.bypassSecurityTrustStyle(transform);
234-
this._markForCheck(() => {
237+
this._markChangeDetectionNeeded(() => {
235238
if (this._renderedContentOffsetNeedsRewrite) {
236239
this._renderedContentOffset -= this.measureRenderedContentSize();
237240
this._renderedContentOffsetNeedsRewrite = false;
@@ -248,7 +251,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
248251
// Rather than setting the offset immediately, we batch it up to be applied along with other DOM
249252
// writes during the next change detection cycle.
250253
this._pendingScrollOffset = offset;
251-
this._markForCheck();
254+
this._markChangeDetectionNeeded();
252255
}
253256

254257
/** Gets the current scroll offset of the viewport (in pixels). */
@@ -289,38 +292,43 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
289292
}
290293

291294
/** Queue up change detection to run. */
292-
private _markForCheck(runAfter?: Function) {
295+
private _markChangeDetectionNeeded(runAfter?: Function) {
293296
if (runAfter) {
294-
this._runAfterCheck.push(runAfter);
297+
this._runAfterChangeDetection.push(runAfter);
295298
}
296-
if (!this._checkPending) {
297-
this._checkPending = true;
299+
300+
// Use a Promise to batch together calls to `_doChangeDetection`. This way if we set a bunch of
301+
// properties sequentially we only have to run `_doChangeDetection` once at the end.
302+
if (!this._isChangeDetectionPending) {
303+
this._isChangeDetectionPending = true;
298304
this._ngZone.runOutsideAngular(() => Promise.resolve().then(() => {
299305
if (this._ngZone.isStable) {
300-
this._doCheck();
306+
this._doChangeDetection();
301307
} else {
302-
this._ngZone.onStable.pipe(take(1)).subscribe(() => this._doCheck());
308+
this._ngZone.onStable.pipe(take(1)).subscribe(() => this._doChangeDetection());
303309
}
304310
}));
305311
}
306312
}
307313

308314
/** Run change detection. */
309-
private _doCheck() {
310-
this._checkPending = false;
315+
private _doChangeDetection() {
316+
this._isChangeDetectionPending = false;
317+
318+
// Apply changes to Angular bindings.
311319
this._ngZone.run(() => this._changeDetectorRef.detectChanges());
312-
// In order to batch setting the scroll offset together with other DOM writes, we wait until a
313-
// change detection cycle to actually apply it.
320+
// Apply the pending scroll offset separately, since it can't be set up as an Angular binding.
314321
if (this._pendingScrollOffset != null) {
315322
if (this.orientation === 'horizontal') {
316323
this.elementRef.nativeElement.scrollLeft = this._pendingScrollOffset;
317324
} else {
318325
this.elementRef.nativeElement.scrollTop = this._pendingScrollOffset;
319326
}
320327
}
321-
for (let fn of this._runAfterCheck) {
328+
329+
for (let fn of this._runAfterChangeDetection) {
322330
fn();
323331
}
324-
this._runAfterCheck = [];
332+
this._runAfterChangeDetection = [];
325333
}
326334
}

0 commit comments

Comments
 (0)