fix(virtual-scroll): fix several small bugs (#13597)

* fix(virtual-scroll): fix several small bugs

fixes include:
* ensure content wrapper is at least as wide as the viewport
* run change detection when data source emits but the data length remains the same
* improve error message when no virtual scroll strategy is provided
* add docs on using a custom virtual scroll strategy

* address comments

* address feedback
This commit is contained in:
mmalerba
2018-10-15 14:02:49 -07:00
committed by Andrew Seguin
parent 160b68877f
commit 8cfaeea4b0
7 changed files with 77 additions and 2 deletions
+11 -1
View File
@@ -25,7 +25,7 @@ rest.
#### Creating items in the viewport
`*cdkVirtualFor` replaces `*ngFor` inside of a `<cdk-virtual-scroll-viewport>`, supporting the exact
same API as [`*ngFor`](https://angular.io/api/common/NgForOf). The simplest usage just specifies the
list of items:
list of items (note that the `itemSize` property on the viewport must be set):
<!-- example(cdk-virtual-scroll-overview) -->
@@ -111,3 +111,13 @@ that the parent does not introduce additional space (e.g. via `margin` or `paddi
interfere with the scrolling.
<!-- example(cdk-virtual-scroll-dl) -->
### Scrolling strategies
In order to determine how large the overall content is and what portion of it actually needs to be
rendered at any given time the viewport relies on a `VirtualScrollStrategy` being provided. The
simplest way to provide it is to use the `itemSize` directive on the viewport
(e.g. `<cdk-virtual-scroll-viewport itemSize="50">`). However it is also possible to provide a
custom strategy by creating a class that implements the `VirtualScrollStrategy` interface and
providing it as the `VIRTUAL_SCROLL_STRATEGY` on the component containing your viewport.
<!-- example(cdk-virtual-scroll-custom-strategy) -->
@@ -56,10 +56,12 @@ cdk-virtual-scroll-viewport {
}
.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper {
min-height: 100%;
@include _cdk-virtual-scroll-clear-container-space(horizontal);
}
.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper {
min-width: 100%;
@include _cdk-virtual-scroll-clear-container-space(vertical);
}
@@ -717,6 +717,20 @@ describe('CdkVirtualScrollViewport', () => {
expect(viewport.elementRef.nativeElement.scrollWidth).toBe(10000);
}));
});
describe('with no VirtualScrollStrategy', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ScrollingModule],
declarations: [VirtualScrollWithNoStrategy],
}).compileComponents();
});
it('should fail on construction', fakeAsync(() => {
expect(() => TestBed.createComponent(VirtualScrollWithNoStrategy)).toThrowError(
'Error: cdk-virtual-scroll-viewport requires the "itemSize" property to be set.');
}));
});
});
@@ -848,3 +862,14 @@ class FixedSizeVirtualScrollWithRtlDirection {
return this.orientation == 'horizontal' ? this.viewportCrossSize : this.viewportSize;
}
}
@Component({
template: `
<cdk-virtual-scroll-viewport>
<div *cdkVirtualFor="let item of items">{{item}}</div>
</cdk-virtual-scroll-viewport>
`
})
class VirtualScrollWithNoStrategy {
items = [];
}
+7 -1
View File
@@ -124,10 +124,15 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O
constructor(public elementRef: ElementRef<HTMLElement>,
private _changeDetectorRef: ChangeDetectorRef,
ngZone: NgZone,
@Inject(VIRTUAL_SCROLL_STRATEGY) private _scrollStrategy: VirtualScrollStrategy,
@Optional() @Inject(VIRTUAL_SCROLL_STRATEGY)
private _scrollStrategy: VirtualScrollStrategy,
@Optional() dir: Directionality,
scrollDispatcher: ScrollDispatcher) {
super(elementRef, scrollDispatcher, ngZone, dir);
if (!_scrollStrategy) {
throw Error('Error: cdk-virtual-scroll-viewport requires the "itemSize" property to be set.');
}
}
ngOnInit() {
@@ -183,6 +188,7 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O
this._dataLength = newLength;
this._scrollStrategy.onDataLengthChanged();
}
this._doChangeDetection();
});
});
}
@@ -0,0 +1,9 @@
.example-viewport {
height: 200px;
width: 200px;
border: 1px solid black;
}
.example-item {
height: 50px;
}
@@ -0,0 +1,3 @@
<cdk-virtual-scroll-viewport class="example-viewport">
<div *cdkVirtualFor="let item of items" class="example-item">{{item}}</div>
</cdk-virtual-scroll-viewport>
@@ -0,0 +1,20 @@
import {FixedSizeVirtualScrollStrategy, VIRTUAL_SCROLL_STRATEGY} from '@angular/cdk/scrolling';
import {ChangeDetectionStrategy, Component} from '@angular/core';
export class CustomVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
constructor() {
super(50, 250, 500);
}
}
/** @title Virtual scroll with a custom strategy */
@Component({
selector: 'cdk-virtual-scroll-custom-strategy-example',
styleUrls: ['cdk-virtual-scroll-custom-strategy-example.css'],
templateUrl: 'cdk-virtual-scroll-custom-strategy-example.html',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [{provide: VIRTUAL_SCROLL_STRATEGY, useClass: CustomVirtualScrollStrategy}]
})
export class CdkVirtualScrollCustomStrategyExample {
items = Array.from({length: 100000}).map((_, i) => `Item #${i}`);
}