mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(virtual-scroll): change fixed strategy to use min & max buffer (#12557)
* feat(virtual-scroll): change fixed strategy to use min & max buffer * address comments
This commit is contained in:
@@ -83,7 +83,7 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
|
||||
private _minBufferPx: number;
|
||||
|
||||
/** The number of buffer items to render beyond the edge of the viewport (in pixels). */
|
||||
private _addBufferPx: number;
|
||||
private _maxBufferPx: number;
|
||||
|
||||
/** The estimator used to estimate the size of unseen items. */
|
||||
private _averager: ItemSizeAverager;
|
||||
@@ -107,14 +107,14 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
|
||||
/**
|
||||
* @param minBufferPx The minimum amount of buffer rendered beyond the viewport (in pixels).
|
||||
* If the amount of buffer dips below this number, more items will be rendered.
|
||||
* @param addBufferPx The number of pixels worth of buffer to shoot for when rendering new items.
|
||||
* @param maxBufferPx The number of pixels worth of buffer to shoot for when rendering new items.
|
||||
* If the actual amount turns out to be less it will not necessarily trigger an additional
|
||||
* rendering cycle (as long as the amount of buffer is still greater than `minBufferPx`).
|
||||
* @param averager The averager used to estimate the size of unseen items.
|
||||
*/
|
||||
constructor(minBufferPx: number, addBufferPx: number, averager = new ItemSizeAverager()) {
|
||||
constructor(minBufferPx: number, maxBufferPx: number, averager = new ItemSizeAverager()) {
|
||||
this._minBufferPx = minBufferPx;
|
||||
this._addBufferPx = addBufferPx;
|
||||
this._maxBufferPx = maxBufferPx;
|
||||
this._averager = averager;
|
||||
}
|
||||
|
||||
@@ -172,12 +172,15 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
|
||||
/**
|
||||
* Update the buffer parameters.
|
||||
* @param minBufferPx The minimum amount of buffer rendered beyond the viewport (in pixels).
|
||||
* @param addBufferPx The number of buffer items to render beyond the edge of the viewport (in
|
||||
* @param maxBufferPx The number of buffer items to render beyond the edge of the viewport (in
|
||||
* pixels).
|
||||
*/
|
||||
updateBufferSize(minBufferPx: number, addBufferPx: number) {
|
||||
updateBufferSize(minBufferPx: number, maxBufferPx: number) {
|
||||
if (maxBufferPx < minBufferPx) {
|
||||
throw('CDK virtual scroll: maxBufferPx must be greater than or equal to minBufferPx');
|
||||
}
|
||||
this._minBufferPx = minBufferPx;
|
||||
this._addBufferPx = addBufferPx;
|
||||
this._maxBufferPx = maxBufferPx;
|
||||
}
|
||||
|
||||
/** Update the rendered content after the user scrolls. */
|
||||
@@ -242,8 +245,8 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
|
||||
} else {
|
||||
// The number of new items to render on the side the user is scrolling towards. Rather than
|
||||
// just filling the underscan space, we actually fill enough to have a buffer size of
|
||||
// `addBufferPx`. This gives us a little wiggle room in case our item size estimate is off.
|
||||
const addItems = Math.max(0, Math.ceil((underscan - this._minBufferPx + this._addBufferPx) /
|
||||
// `maxBufferPx`. This gives us a little wiggle room in case our item size estimate is off.
|
||||
const addItems = Math.max(0, Math.ceil((underscan - this._minBufferPx + this._maxBufferPx) /
|
||||
this._averager.getAverageItemSize()));
|
||||
// The amount of filled space beyond what is necessary on the side the user is scrolling
|
||||
// away from.
|
||||
@@ -361,7 +364,7 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
|
||||
const itemSize = this._averager.getAverageItemSize();
|
||||
const firstVisibleIndex =
|
||||
Math.min(viewport.getDataLength() - 1, Math.floor(scrollOffset / itemSize));
|
||||
const bufferSize = Math.ceil(this._addBufferPx / itemSize);
|
||||
const bufferSize = Math.ceil(this._maxBufferPx / itemSize);
|
||||
const range = this._expandRange(
|
||||
this._getVisibleRangeForIndex(firstVisibleIndex), bufferSize, bufferSize);
|
||||
|
||||
@@ -456,14 +459,14 @@ export class CdkAutoSizeVirtualScroll implements OnChanges {
|
||||
* Defaults to 200px.
|
||||
*/
|
||||
@Input()
|
||||
get addBufferPx(): number { return this._addBufferPx; }
|
||||
set addBufferPx(value: number) { this._addBufferPx = coerceNumberProperty(value); }
|
||||
_addBufferPx = 200;
|
||||
get maxBufferPx(): number { return this._maxBufferPx; }
|
||||
set maxBufferPx(value: number) { this._maxBufferPx = coerceNumberProperty(value); }
|
||||
_maxBufferPx = 200;
|
||||
|
||||
/** The scroll strategy used by this directive. */
|
||||
_scrollStrategy = new AutoSizeVirtualScrollStrategy(this.minBufferPx, this.addBufferPx);
|
||||
_scrollStrategy = new AutoSizeVirtualScrollStrategy(this.minBufferPx, this.maxBufferPx);
|
||||
|
||||
ngOnChanges() {
|
||||
this._scrollStrategy.updateBufferSize(this.minBufferPx, this.addBufferPx);
|
||||
this._scrollStrategy.updateBufferSize(this.minBufferPx, this.maxBufferPx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,19 +11,19 @@ This can be added to your viewport by using the `autosize` directive.
|
||||
</cdk-virtual-scroll-viewport>
|
||||
```
|
||||
|
||||
The `autosize` strategy is configured through two inputs: `minBufferPx` and `addBufferPx`.
|
||||
The `autosize` strategy is configured through two inputs: `minBufferPx` and `maxBufferPx`.
|
||||
|
||||
**`minBufferPx`** determines the minimum space outside virtual scrolling viewport that will be
|
||||
filled with content. Increasing this will increase the amount of content a user will see before more
|
||||
content must be rendered. However, too large a value will cause more content to be rendered than is
|
||||
necessary.
|
||||
|
||||
**`addBufferPx`** determines the amount of content that will be added incrementally as the viewport
|
||||
**`maxBufferPx`** determines the amount of content that will be added incrementally as the viewport
|
||||
is scrolled. This should be greater than the size of `minBufferPx` so that one "render" is needed at
|
||||
a time.
|
||||
|
||||
```html
|
||||
<cdk-virtual-scroll-viewport autosize minBufferPx="50" addBufferPx="100">
|
||||
<cdk-virtual-scroll-viewport autosize minBufferPx="50" maxBufferPx="100">
|
||||
...
|
||||
</cdk-virtual-scroll-viewport>
|
||||
```
|
||||
|
||||
@@ -40,6 +40,12 @@ describe('CdkVirtualScrollViewport', () => {
|
||||
'should render 4 items to fill 200px space based on 50px estimate from first item');
|
||||
}));
|
||||
|
||||
it('should throw if maxBufferPx is less than minBufferPx', fakeAsync(() => {
|
||||
testComponent.minBufferPx = 100;
|
||||
testComponent.maxBufferPx = 99;
|
||||
expect(() => finishInit(fixture)).toThrow();
|
||||
}));
|
||||
|
||||
// TODO(mmalerba): Add test that it corrects the initial render if it didn't render enough,
|
||||
// once it actually does that.
|
||||
});
|
||||
@@ -61,7 +67,7 @@ function finishInit(fixture: ComponentFixture<any>) {
|
||||
@Component({
|
||||
template: `
|
||||
<cdk-virtual-scroll-viewport
|
||||
autosize [minBufferPx]="minBufferSize" [addBufferPx]="addBufferSize"
|
||||
autosize [minBufferPx]="minBufferPx" [maxBufferPx]="maxBufferPx"
|
||||
[orientation]="orientation" [style.height.px]="viewportHeight"
|
||||
[style.width.px]="viewportWidth">
|
||||
<div class="item" *cdkVirtualFor="let size of items; let i = index" [style.height.px]="size"
|
||||
@@ -88,8 +94,8 @@ class AutoSizeVirtualScroll {
|
||||
@Input() orientation = 'vertical';
|
||||
@Input() viewportSize = 200;
|
||||
@Input() viewportCrossSize = 100;
|
||||
@Input() minBufferSize = 0;
|
||||
@Input() addBufferSize = 0;
|
||||
@Input() minBufferPx = 0;
|
||||
@Input() maxBufferPx = 0;
|
||||
@Input() items = Array(10).fill(50);
|
||||
|
||||
get viewportWidth() {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
import {coerceNumberProperty} from '@angular/cdk/coercion';
|
||||
import {ListRange} from '@angular/cdk/collections';
|
||||
import {Directive, forwardRef, Input, OnChanges} from '@angular/core';
|
||||
import {Observable, Subject} from 'rxjs';
|
||||
import {distinctUntilChanged} from 'rxjs/operators';
|
||||
@@ -28,16 +27,21 @@ export class FixedSizeVirtualScrollStrategy implements VirtualScrollStrategy {
|
||||
/** The size of the items in the virtually scrolling list. */
|
||||
private _itemSize: number;
|
||||
|
||||
/** The number of buffer items to render beyond the edge of the viewport. */
|
||||
private _bufferSize: number;
|
||||
/** The minimum amount of buffer rendered beyond the viewport (in pixels). */
|
||||
private _minBufferPx: number;
|
||||
|
||||
/** The number of buffer items to render beyond the edge of the viewport (in pixels). */
|
||||
private _maxBufferPx: number;
|
||||
|
||||
/**
|
||||
* @param itemSize The size of the items in the virtually scrolling list.
|
||||
* @param bufferSize The number of buffer items to render beyond the edge of the viewport.
|
||||
* @param minBufferPx The minimum amount of buffer (in pixels) before needing to render more
|
||||
* @param maxBufferPx The amount of buffer (in pixels) to render when rendering more.
|
||||
*/
|
||||
constructor(itemSize: number, bufferSize: number) {
|
||||
constructor(itemSize: number, minBufferPx: number, maxBufferPx: number) {
|
||||
this._itemSize = itemSize;
|
||||
this._bufferSize = bufferSize;
|
||||
this._minBufferPx = minBufferPx;
|
||||
this._maxBufferPx = maxBufferPx;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,11 +63,16 @@ export class FixedSizeVirtualScrollStrategy implements VirtualScrollStrategy {
|
||||
/**
|
||||
* Update the item size and buffer size.
|
||||
* @param itemSize The size of the items in the virtually scrolling list.
|
||||
* @param bufferSize he number of buffer items to render beyond the edge of the viewport.
|
||||
* @param minBufferPx The minimum amount of buffer (in pixels) before needing to render more
|
||||
* @param maxBufferPx The amount of buffer (in pixels) to render when rendering more.
|
||||
*/
|
||||
updateItemAndBufferSize(itemSize: number, bufferSize: number) {
|
||||
updateItemAndBufferSize(itemSize: number, minBufferPx: number, maxBufferPx: number) {
|
||||
if (maxBufferPx < minBufferPx) {
|
||||
throw Error('CDK virtual scroll: maxBufferPx must be greater than or equal to minBufferPx');
|
||||
}
|
||||
this._itemSize = itemSize;
|
||||
this._bufferSize = bufferSize;
|
||||
this._minBufferPx = minBufferPx;
|
||||
this._maxBufferPx = maxBufferPx;
|
||||
this._updateTotalContentSize();
|
||||
this._updateRenderedRange();
|
||||
}
|
||||
@@ -112,34 +121,33 @@ export class FixedSizeVirtualScrollStrategy implements VirtualScrollStrategy {
|
||||
}
|
||||
|
||||
const scrollOffset = this._viewport.measureScrollOffset();
|
||||
const firstVisibleIndex = Math.floor(scrollOffset / this._itemSize);
|
||||
const firstItemRemainder = scrollOffset % this._itemSize;
|
||||
const range = this._expandRange(
|
||||
{start: firstVisibleIndex, end: firstVisibleIndex},
|
||||
this._bufferSize,
|
||||
Math.ceil((this._viewport.getViewportSize() + firstItemRemainder) / this._itemSize) +
|
||||
this._bufferSize);
|
||||
this._viewport.setRenderedRange(range);
|
||||
this._viewport.setRenderedContentOffset(this._itemSize * range.start);
|
||||
const firstVisibleIndex = scrollOffset / this._itemSize;
|
||||
const renderedRange = this._viewport.getRenderedRange();
|
||||
const newRange = {start: renderedRange.start, end: renderedRange.end};
|
||||
const viewportSize = this._viewport.getViewportSize();
|
||||
const dataLength = this._viewport.getDataLength();
|
||||
|
||||
this._scrolledIndexChange.next(firstVisibleIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the given range by the given amount in either direction.
|
||||
* @param range The range to expand
|
||||
* @param expandStart The number of items to expand the start of the range by.
|
||||
* @param expandEnd The number of items to expand the end of the range by.
|
||||
* @return The expanded range.
|
||||
*/
|
||||
private _expandRange(range: ListRange, expandStart: number, expandEnd: number): ListRange {
|
||||
if (!this._viewport) {
|
||||
return {...range};
|
||||
const startBuffer = scrollOffset - newRange.start * this._itemSize;
|
||||
if (startBuffer < this._minBufferPx && newRange.start != 0) {
|
||||
const expandStart = Math.ceil((this._maxBufferPx - startBuffer) / this._itemSize);
|
||||
newRange.start = Math.max(0, newRange.start - expandStart);
|
||||
newRange.end = Math.min(dataLength,
|
||||
Math.ceil(firstVisibleIndex + (viewportSize + this._minBufferPx) / this._itemSize));
|
||||
} else {
|
||||
const endBuffer = newRange.end * this._itemSize - (scrollOffset + viewportSize);
|
||||
if (endBuffer < this._minBufferPx && newRange.end != dataLength) {
|
||||
const expandEnd = Math.ceil((this._maxBufferPx - endBuffer) / this._itemSize);
|
||||
if (expandEnd > 0) {
|
||||
newRange.end = Math.min(dataLength, newRange.end + expandEnd);
|
||||
newRange.start = Math.max(0,
|
||||
Math.floor(firstVisibleIndex - this._minBufferPx / this._itemSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const start = Math.max(0, range.start - expandStart);
|
||||
const end = Math.min(this._viewport.getDataLength(), range.end + expandEnd);
|
||||
return {start, end};
|
||||
this._viewport.setRenderedRange(newRange);
|
||||
this._viewport.setRenderedContentOffset(this._itemSize * newRange.start);
|
||||
this._scrolledIndexChange.next(Math.floor(firstVisibleIndex));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,18 +180,27 @@ export class CdkFixedSizeVirtualScroll implements OnChanges {
|
||||
_itemSize = 20;
|
||||
|
||||
/**
|
||||
* The number of extra elements to render on either side of the scrolling viewport.
|
||||
* Defaults to 5 elements.
|
||||
* The minimum amount of buffer rendered beyond the viewport (in pixels).
|
||||
* If the amount of buffer dips below this number, more items will be rendered. Defaults to 100px.
|
||||
*/
|
||||
@Input()
|
||||
get bufferSize(): number { return this._bufferSize; }
|
||||
set bufferSize(value: number) { this._bufferSize = coerceNumberProperty(value); }
|
||||
_bufferSize = 5;
|
||||
get minBufferPx(): number { return this._minBufferPx; }
|
||||
set minBufferPx(value: number) { this._minBufferPx = coerceNumberProperty(value); }
|
||||
_minBufferPx = 100;
|
||||
|
||||
/**
|
||||
* The number of pixels worth of buffer to render for when rendering new items. Defaults to 200px.
|
||||
*/
|
||||
@Input()
|
||||
get maxBufferPx(): number { return this._maxBufferPx; }
|
||||
set maxBufferPx(value: number) { this._maxBufferPx = coerceNumberProperty(value); }
|
||||
_maxBufferPx = 200;
|
||||
|
||||
/** The scroll strategy used by this directive. */
|
||||
_scrollStrategy = new FixedSizeVirtualScrollStrategy(this.itemSize, this.bufferSize);
|
||||
_scrollStrategy =
|
||||
new FixedSizeVirtualScrollStrategy(this.itemSize, this.minBufferPx, this.maxBufferPx);
|
||||
|
||||
ngOnChanges() {
|
||||
this._scrollStrategy.updateItemAndBufferSize(this.itemSize, this.bufferSize);
|
||||
this._scrollStrategy.updateItemAndBufferSize(this.itemSize, this.minBufferPx, this.maxBufferPx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,12 +94,22 @@ that it allows for better performance, since items do not need to be measured as
|
||||
</cdk-virtual-scroll-viewport>
|
||||
```
|
||||
|
||||
The fixed size strategy also supports setting the buffer size, i.e. the number of items rendered
|
||||
beyond the edge of the viewport. This can be adjusted by setting the `bufferSize` input. If
|
||||
`bufferSize` is not specified it defaults to 5 items.
|
||||
The fixed size strategy also supports setting a couple of buffer parameters that determine how much
|
||||
extra content is rendered beyond what is visible in the viewport. The first of these parameters is
|
||||
`minBufferPx`. The `minBufferPx` is the minimum amount of content buffer (in pixels) that the
|
||||
viewport must render. If the viewport ever detects that there is less buffered content it will
|
||||
immediately render more. The second buffer parameter is `maxBufferPx`. This tells the viewport how
|
||||
much buffer space to render back up to when it detects that more buffer is required.
|
||||
|
||||
The interaction of these two buffer parameters can be best illustrated with an example. Supposed
|
||||
that we have the following parameters: `itemSize = 50`, `minBufferPx = 100`, `maxBufferPx = 250`. As
|
||||
the user is scrolling through the content the viewport detects that there is only `90px` of buffer
|
||||
remaining. Since this is below `minBufferPx` the viewport must render more buffer. It must render at
|
||||
least enough buffer to get back to `maxBufferPx`. In this case, it renders 4 items (an additional
|
||||
`200px`) to bring the total buffer size to `290px`, back above `maxBufferPx`.
|
||||
|
||||
```html
|
||||
<cdk-virtual-scroll-viewport itemSize="50" bufferSize="1">
|
||||
<cdk-virtual-scroll-viewport itemSize="50" minBufferPx="100" maxBufferPx="250">
|
||||
...
|
||||
</cdk-virtual-scroll-viewport>
|
||||
```
|
||||
|
||||
@@ -226,7 +226,7 @@ describe('CdkVirtualScrollViewport', () => {
|
||||
|
||||
const maxOffset =
|
||||
testComponent.itemSize * testComponent.items.length - testComponent.viewportSize;
|
||||
for (let offset = 0; offset <= maxOffset; offset += 10) {
|
||||
for (let offset = 1; offset <= maxOffset; offset += 10) {
|
||||
triggerScroll(viewport, offset);
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
@@ -252,7 +252,7 @@ describe('CdkVirtualScrollViewport', () => {
|
||||
|
||||
const maxOffset =
|
||||
testComponent.itemSize * testComponent.items.length - testComponent.viewportSize;
|
||||
for (let offset = maxOffset; offset >= 0; offset -= 10) {
|
||||
for (let offset = maxOffset - 1; offset >= 0; offset -= 10) {
|
||||
triggerScroll(viewport, offset);
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
@@ -274,7 +274,8 @@ describe('CdkVirtualScrollViewport', () => {
|
||||
}));
|
||||
|
||||
it('should render buffer element at the end when scrolled to the top', fakeAsync(() => {
|
||||
testComponent.bufferSize = 1;
|
||||
testComponent.minBufferPx = testComponent.itemSize;
|
||||
testComponent.maxBufferPx = testComponent.itemSize;
|
||||
finishInit(fixture);
|
||||
|
||||
expect(viewport.getRenderedRange()).toEqual({start: 0, end: 5},
|
||||
@@ -284,7 +285,8 @@ describe('CdkVirtualScrollViewport', () => {
|
||||
|
||||
it('should render buffer element at the start and end when scrolled to the middle',
|
||||
fakeAsync(() => {
|
||||
testComponent.bufferSize = 1;
|
||||
testComponent.minBufferPx = testComponent.itemSize;
|
||||
testComponent.maxBufferPx = testComponent.itemSize;
|
||||
finishInit(fixture);
|
||||
triggerScroll(viewport, testComponent.itemSize * 2);
|
||||
fixture.detectChanges();
|
||||
@@ -296,7 +298,8 @@ describe('CdkVirtualScrollViewport', () => {
|
||||
}));
|
||||
|
||||
it('should render buffer element at the start when scrolled to the bottom', fakeAsync(() => {
|
||||
testComponent.bufferSize = 1;
|
||||
testComponent.minBufferPx = testComponent.itemSize;
|
||||
testComponent.maxBufferPx = testComponent.itemSize;
|
||||
finishInit(fixture);
|
||||
triggerScroll(viewport, testComponent.itemSize * 6);
|
||||
fixture.detectChanges();
|
||||
@@ -333,7 +336,8 @@ describe('CdkVirtualScrollViewport', () => {
|
||||
expect(viewport.getRenderedRange())
|
||||
.toEqual({start: 2, end: 6}, 'should render 4 50px items to fill 200px space');
|
||||
|
||||
testComponent.bufferSize = 1;
|
||||
testComponent.minBufferPx = testComponent.itemSize;
|
||||
testComponent.maxBufferPx = testComponent.itemSize;
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
|
||||
@@ -368,7 +372,7 @@ describe('CdkVirtualScrollViewport', () => {
|
||||
|
||||
const maxOffset =
|
||||
testComponent.itemSize * testComponent.items.length - testComponent.viewportSize;
|
||||
for (let offset = 0; offset <= maxOffset; offset += 10) {
|
||||
for (let offset = 1; offset <= maxOffset; offset += 10) {
|
||||
triggerScroll(viewport, offset);
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
@@ -395,7 +399,7 @@ describe('CdkVirtualScrollViewport', () => {
|
||||
|
||||
const maxOffset =
|
||||
testComponent.itemSize * testComponent.items.length - testComponent.viewportSize;
|
||||
for (let offset = maxOffset; offset >= 0; offset -= 10) {
|
||||
for (let offset = maxOffset - 1; offset >= 0; offset -= 10) {
|
||||
triggerScroll(viewport, offset);
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
@@ -551,6 +555,35 @@ describe('CdkVirtualScrollViewport', () => {
|
||||
// create a new view for each one.
|
||||
expect(testComponent.virtualForViewContainer.createEmbeddedView).toHaveBeenCalledTimes(5);
|
||||
}));
|
||||
|
||||
it('should render up to maxBufferPx when buffer dips below minBufferPx', fakeAsync(() => {
|
||||
testComponent.minBufferPx = testComponent.itemSize;
|
||||
testComponent.maxBufferPx = testComponent.itemSize * 2;
|
||||
finishInit(fixture);
|
||||
|
||||
expect(viewport.getRenderedRange())
|
||||
.toEqual({start: 0, end: 6}, 'should have 2 buffer items initially');
|
||||
|
||||
triggerScroll(viewport, 50);
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
|
||||
expect(viewport.getRenderedRange())
|
||||
.toEqual({start: 0, end: 6}, 'should not render additional buffer yet');
|
||||
|
||||
triggerScroll(viewport, 51);
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
|
||||
expect(viewport.getRenderedRange())
|
||||
.toEqual({start: 0, end: 8}, 'should render 2 more buffer items');
|
||||
}));
|
||||
|
||||
it('should throw if maxBufferPx is less than minBufferPx', fakeAsync(() => {
|
||||
testComponent.minBufferPx = 100;
|
||||
testComponent.maxBufferPx = 99;
|
||||
expect(() => finishInit(fixture)).toThrow();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -583,9 +616,9 @@ function triggerScroll(viewport: CdkVirtualScrollViewport, offset?: number) {
|
||||
@Component({
|
||||
template: `
|
||||
<cdk-virtual-scroll-viewport
|
||||
[itemSize]="itemSize" [bufferSize]="bufferSize" [orientation]="orientation"
|
||||
[style.height.px]="viewportHeight" [style.width.px]="viewportWidth"
|
||||
(scrolledIndexChange)="scrolledToIndex = $event">
|
||||
[itemSize]="itemSize" [minBufferPx]="minBufferPx" [maxBufferPx]="maxBufferPx"
|
||||
[orientation]="orientation" [style.height.px]="viewportHeight"
|
||||
[style.width.px]="viewportWidth" (scrolledIndexChange)="scrolledToIndex = $event">
|
||||
<div class="item"
|
||||
*cdkVirtualFor="let item of items; let i = index; trackBy: trackBy; \
|
||||
templateCacheSize: templateCacheSize"
|
||||
@@ -614,7 +647,8 @@ class FixedSizeVirtualScroll {
|
||||
@Input() viewportSize = 200;
|
||||
@Input() viewportCrossSize = 100;
|
||||
@Input() itemSize = 50;
|
||||
@Input() bufferSize = 0;
|
||||
@Input() minBufferPx = 0;
|
||||
@Input() maxBufferPx = 0;
|
||||
@Input() items = Array(10).fill(0).map((_, i) => i);
|
||||
@Input() trackBy;
|
||||
@Input() templateCacheSize = 20;
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
ViewEncapsulation,
|
||||
} from '@angular/core';
|
||||
import {animationFrameScheduler, fromEvent, Observable, Subject} from 'rxjs';
|
||||
import {sample, sampleTime, takeUntil} from 'rxjs/operators';
|
||||
import {sampleTime, takeUntil} from 'rxjs/operators';
|
||||
import {CdkVirtualForOf} from './virtual-for-of';
|
||||
import {VIRTUAL_SCROLL_STRATEGY, VirtualScrollStrategy} from './virtual-scroll-strategy';
|
||||
|
||||
@@ -55,9 +55,6 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
|
||||
/** Emits when the rendered range changes. */
|
||||
private _renderedRangeSubject = new Subject<ListRange>();
|
||||
|
||||
/** Emits when a change detection cycle completes. */
|
||||
private _changeDetectionComplete = new Subject<void>();
|
||||
|
||||
/** The direction the viewport scrolls. */
|
||||
@Input() orientation: 'horizontal' | 'vertical' = 'vertical';
|
||||
|
||||
@@ -66,10 +63,9 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
|
||||
// depending on how the strategy calculates the scrolled index, it may come at a cost to
|
||||
// performance.
|
||||
/** Emits when the index of the first element visible in the viewport changes. */
|
||||
@Output() scrolledIndexChange: Observable<number> =
|
||||
Observable.create(observer => this._scrollStrategy.scrolledIndexChange
|
||||
.pipe(sample(this._changeDetectionComplete))
|
||||
.subscribe(observer));
|
||||
@Output() scrolledIndexChange: Observable<number> = Observable.create(observer =>
|
||||
this._scrollStrategy.scrolledIndexChange.subscribe(index =>
|
||||
Promise.resolve().then(() => this._ngZone.run(() => observer.next(index)))));
|
||||
|
||||
/** The element that wraps the rendered content. */
|
||||
@ViewChild('contentWrapper') _contentWrapper: ElementRef<HTMLElement>;
|
||||
@@ -159,7 +155,6 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
|
||||
// Complete all subjects
|
||||
this._renderedRangeSubject.complete();
|
||||
this._detachedSubject.complete();
|
||||
this._changeDetectionComplete.complete();
|
||||
this._destroyed.complete();
|
||||
}
|
||||
|
||||
@@ -386,7 +381,5 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
|
||||
for (const fn of runAfterChangeDetection) {
|
||||
fn();
|
||||
}
|
||||
|
||||
this._ngZone.run(() => this._changeDetectionComplete.next());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user