Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions goldens/cdk/scrolling/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ export type _Bottom = {
bottom?: number;
};

// @public
export const CDK_VIRTUAL_SCROLL_VIEWPORT: InjectionToken<CdkVirtualScrollViewport>;

// @public
export class CdkFixedSizeVirtualScroll implements OnChanges {
get itemSize(): number;
Expand Down Expand Up @@ -199,6 +196,7 @@ export class CdkVirtualScrollViewport extends CdkVirtualScrollable implements On
ngOnInit(): void;
get orientation(): "horizontal" | "vertical";
set orientation(orientation: 'horizontal' | 'vertical');
readonly renderedContentOffset: Observable<number>;
readonly renderedRangeStream: Observable<ListRange>;
// (undocumented)
scrollable: CdkVirtualScrollable;
Expand Down
19 changes: 11 additions & 8 deletions goldens/cdk/table/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ export class CdkRowDef<T> extends BaseRowDef {
}

// @public
export class CdkTable<T> implements AfterContentInit, AfterContentChecked, CollectionViewer, OnDestroy, OnInit {
export class CdkTable<T> implements AfterContentInit, AfterContentChecked, CollectionViewer, OnDestroy, OnInit, StickyPositioningListener {
constructor(...args: unknown[]);
addColumnDef(columnDef: CdkColumnDef): void;
addFooterRowDef(footerRowDef: CdkFooterRowDef): void;
Expand All @@ -307,6 +307,8 @@ export class CdkTable<T> implements AfterContentInit, AfterContentChecked, Colle
protected _data: readonly T[] | undefined;
get dataSource(): CdkTableDataSourceInput<T>;
set dataSource(dataSource: CdkTableDataSourceInput<T>);
readonly _dataSourceChanges: Subject<CdkTableDataSourceInput<T>>;
readonly _dataStream: Subject<readonly T[]>;
// (undocumented)
protected readonly _differs: IterableDiffers;
// (undocumented)
Expand Down Expand Up @@ -352,26 +354,27 @@ export class CdkTable<T> implements AfterContentInit, AfterContentChecked, Colle
removeFooterRowDef(footerRowDef: CdkFooterRowDef): void;
removeHeaderRowDef(headerRowDef: CdkHeaderRowDef): void;
removeRowDef(rowDef: CdkRowDef<T>): void;
protected _renderedRange?: ListRange;
renderRows(): void;
// (undocumented)
_rowOutlet: DataRowOutlet;
setNoDataRow(noDataRow: CdkNoDataRow | null): void;
stickyColumnsUpdated(update: StickyUpdate): void;
protected stickyCssClass: string;
// (undocumented)
protected readonly _stickyPositioningListener: StickyPositioningListener;
stickyEndColumnsUpdated(update: StickyUpdate): void;
stickyFooterRowsUpdated(update: StickyUpdate): void;
stickyHeaderRowsUpdated(update: StickyUpdate): void;
get trackBy(): TrackByFunction<T>;
set trackBy(fn: TrackByFunction<T>);
updateStickyColumnStyles(): void;
updateStickyFooterRowStyles(): void;
updateStickyHeaderRowStyles(): void;
readonly viewChange: BehaviorSubject<{
start: number;
end: number;
}>;
readonly viewChange: BehaviorSubject<ListRange>;
// (undocumented)
protected _viewRepeater: _ViewRepeater<T, RenderRow<T>, RowContext<T>>;
virtualScroll: CdkVirtualScrollViewport | null;
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<CdkTable<any>, "cdk-table, table[cdk-table]", ["cdkTable"], { "trackBy": { "alias": "trackBy"; "required": false; }; "dataSource": { "alias": "dataSource"; "required": false; }; "multiTemplateDataRows": { "alias": "multiTemplateDataRows"; "required": false; }; "fixedLayout": { "alias": "fixedLayout"; "required": false; }; "recycleRows": { "alias": "recycleRows"; "required": false; }; }, { "contentChanged": "contentChanged"; }, ["_noDataRow", "_contentColumnDefs", "_contentRowDefs", "_contentHeaderRowDefs", "_contentFooterRowDefs"], ["caption", "colgroup, col", "*"], true, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<CdkTable<any>, "cdk-table, table[cdk-table]", ["cdkTable"], { "trackBy": { "alias": "trackBy"; "required": false; }; "dataSource": { "alias": "dataSource"; "required": false; }; "multiTemplateDataRows": { "alias": "multiTemplateDataRows"; "required": false; }; "fixedLayout": { "alias": "fixedLayout"; "required": false; }; "recycleRows": { "alias": "recycleRows"; "required": false; }; "virtualScroll": { "alias": "virtualScroll"; "required": false; }; }, { "contentChanged": "contentChanged"; }, ["_noDataRow", "_contentColumnDefs", "_contentRowDefs", "_contentHeaderRowDefs", "_contentFooterRowDefs"], ["caption", "colgroup, col", "*"], true, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<CdkTable<any>, never>;
}
Expand Down
4 changes: 2 additions & 2 deletions src/cdk/scrolling/virtual-for-of.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {NumberInput, coerceNumberProperty} from '../coercion';
import {Observable, Subject, of as observableOf, isObservable} from 'rxjs';
import {pairwise, shareReplay, startWith, switchMap, takeUntil} from 'rxjs/operators';
import {CdkVirtualScrollRepeater} from './virtual-scroll-repeater';
import {CDK_VIRTUAL_SCROLL_VIEWPORT} from './virtual-scroll-viewport';
import {CdkVirtualScrollViewport} from './virtual-scroll-viewport';

/** The context for an item rendered by `CdkVirtualForOf` */
export type CdkVirtualForOfContext<T> = {
Expand Down Expand Up @@ -87,7 +87,7 @@ export class CdkVirtualForOf<T>
private _template = inject<TemplateRef<CdkVirtualForOfContext<T>>>(TemplateRef);
private _differs = inject(IterableDiffers);
private _viewRepeater = new _RecycleViewRepeaterStrategy<T, T, CdkVirtualForOfContext<T>>();
private _viewport = inject(CDK_VIRTUAL_SCROLL_VIEWPORT, {skipSelf: true});
private _viewport = inject(CdkVirtualScrollViewport, {skipSelf: true});

/** Emits when the rendered view of the data changes. */
readonly viewChange = new Subject<ListRange>();
Expand Down
22 changes: 11 additions & 11 deletions src/cdk/scrolling/virtual-scroll-viewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
effect,
ElementRef,
inject,
InjectionToken,
Injector,
Input,
OnDestroy,
Expand All @@ -38,7 +37,7 @@ import {
Subject,
Subscription,
} from 'rxjs';
import {auditTime, startWith, takeUntil} from 'rxjs/operators';
import {auditTime, distinctUntilChanged, filter, startWith, takeUntil} from 'rxjs/operators';
import {CdkScrollable, ExtendedScrollToOptions} from './scrollable';
import {ViewportRuler} from './viewport-ruler';
import {CdkVirtualScrollRepeater} from './virtual-scroll-repeater';
Expand All @@ -58,14 +57,6 @@ function rangesEqual(r1: ListRange, r2: ListRange): boolean {
const SCROLL_SCHEDULER =
typeof requestAnimationFrame !== 'undefined' ? animationFrameScheduler : asapScheduler;

/**
* Lightweight token that can be used to inject the `CdkVirtualScrollViewport`
* without introducing a hard dependency on it.
*/
export const CDK_VIRTUAL_SCROLL_VIEWPORT = new InjectionToken<CdkVirtualScrollViewport>(
'CDK_VIRTUAL_SCROLL_VIEWPORT',
);

/** A viewport that virtualizes its scrolling with the help of `CdkVirtualForOf`. */
@Component({
selector: 'cdk-virtual-scroll-viewport',
Expand All @@ -84,7 +75,6 @@ export const CDK_VIRTUAL_SCROLL_VIEWPORT = new InjectionToken<CdkVirtualScrollVi
useFactory: () =>
inject(VIRTUAL_SCROLLABLE, {optional: true}) || inject(CdkVirtualScrollViewport),
},
{provide: CDK_VIRTUAL_SCROLL_VIEWPORT, useExisting: CdkVirtualScrollViewport},
],
})
export class CdkVirtualScrollViewport extends CdkVirtualScrollable implements OnInit, OnDestroy {
Expand All @@ -102,6 +92,7 @@ export class CdkVirtualScrollViewport extends CdkVirtualScrollable implements On

/** Emits when the rendered range changes. */
private readonly _renderedRangeSubject = new Subject<ListRange>();
private readonly _renderedContentOffsetSubject = new Subject<number | null>();

/** The direction the viewport scrolls. */
@Input()
Expand Down Expand Up @@ -141,6 +132,14 @@ export class CdkVirtualScrollViewport extends CdkVirtualScrollable implements On
/** A stream that emits whenever the rendered range changes. */
readonly renderedRangeStream: Observable<ListRange> = this._renderedRangeSubject;

/**
* Emits the offset from the start of the viewport to the start of the rendered data (in pixels).
*/
readonly renderedContentOffset: Observable<number> = this._renderedContentOffsetSubject.pipe(
filter(offset => offset !== null),
distinctUntilChanged(),
);

/**
* The total size of all content (in pixels), including content that is not currently rendered.
*/
Expand Down Expand Up @@ -537,6 +536,7 @@ export class CdkVirtualScrollViewport extends CdkVirtualScrollable implements On
// string literals, a variable that can only be 'X' or 'Y', and user input that is run through
// the `Number` function first to coerce it to a numeric value.
this._contentWrapper.nativeElement.style.transform = this._renderedContentTransform;
this._renderedContentOffsetSubject.next(this.getOffsetToRenderedContentStart());

afterNextRender(
() => {
Expand Down
2 changes: 2 additions & 0 deletions src/cdk/table/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ ng_project(
"//:node_modules/rxjs",
"//src/cdk/bidi",
"//src/cdk/collections",
"//src/cdk/scrolling",
"//src/cdk/testing/private",
],
)

Expand Down
4 changes: 3 additions & 1 deletion src/cdk/table/sticky-position-listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import {InjectionToken} from '@angular/core';

/** The injection token used to specify the StickyPositioningListener. */
export const STICKY_POSITIONING_LISTENER = new InjectionToken<StickyPositioningListener>('CDK_SPL');
export const STICKY_POSITIONING_LISTENER = new InjectionToken<StickyPositioningListener>(
'STICKY_POSITIONING_LISTENER',
);

export type StickySize = number | null | undefined;
export type StickyOffset = number | null | undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/cdk/table/sticky-styler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class StickyStyler {
private _isBrowser = true,
private readonly _needsPositionStickyOnElement = true,
public direction: Direction,
private readonly _positionListener: StickyPositioningListener,
private readonly _positionListener: StickyPositioningListener | null,
private readonly _tableInjector: Injector,
) {
this._borderCellCss = {
Expand Down
23 changes: 16 additions & 7 deletions src/cdk/table/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ top of the CDK data-table.
The first step to writing the data-table template is to define the columns.
A column definition is specified via an `<ng-container>` with the `cdkColumnDef` directive, giving
the column a name. Each column definition can contain a header-cell template
(`cdkHeaderCellDef`), data-cell template (`cdkCellDef`), and footer-cell
(`cdkHeaderCellDef`), data-cell template (`cdkCellDef`), and footer-cell
template (`cdkFooterCellDef`).

```html
Expand Down Expand Up @@ -120,9 +120,9 @@ cells that are displayed in the column `name` will be given the class `cdk-colum
columns to be given styles that will match across the header and rows.

Since columns can be given any string for its name, its possible that it cannot be directly applied
to the CSS class (e.g. `*nameColumn!`). In these cases, the special characters will be replaced by
to the CSS class (e.g. `*nameColumn!`). In these cases, the special characters will be replaced by
the `-` character. For example, cells container in a column named `*nameColumn!` will be given
the class `cdk-column--nameColumn-`.
the class `cdk-column--nameColumn-`.

#### Connecting the table to a data source

Expand Down Expand Up @@ -158,23 +158,32 @@ table how to uniquely identify rows to track how the data changes with each upda
```

##### `recycleRows`
By default, `CdkTable` creates and destroys an internal Angular view for each row. This allows rows
to participate in animations and to toggle between different row templates with `cdkRowDefWhen`. If
you don't need these features, you can instruct the table to cache and recycle rows by specifying
By default, `CdkTable` creates and destroys an internal Angular view for each row. This allows rows
to participate in animations and to toggle between different row templates with `cdkRowDefWhen`. If
you don't need these features, you can instruct the table to cache and recycle rows by specifying
`recycleRows`.

```html
<table cdk-table [dataSource]="dataSource" recycleRows>
```

### Virtual scrolling

If you're showing a large amount of data in your table, you can use virtual scrolling to ensure a
smooth experience for the user. To enable virtual scrolling, you have to wrap the CDK table in a
`cdk-virtual-scroll-viewport` element, pass the viewport to the table and add some CSS to make it
scrollable.

<!-- example(cdk-table-virtual-scroll) -->

### Alternate HTML to using native table

The CDK table does not require that you use a native HTML table. If you want to have full control
over the style of the table, it may be easier to follow an alternative template approach that does
not use the native table element tags.

This alternative approach replaces the native table element tags with the CDK table directive
selectors. For example, `<table cdk-table>` becomes `<cdk-table>`; `<tr cdk-row`> becomes
selectors. For example, `<table cdk-table>` becomes `<cdk-table>`; `<tr cdk-row`> becomes
`<cdk-row>`. The following shows a previous example using this alternative template:

```html
Expand Down
Loading
Loading