Skip to content

Commit 48c0a70

Browse files
committed
address some comments
1 parent 27802dc commit 48c0a70

File tree

8 files changed

+131
-83
lines changed

8 files changed

+131
-83
lines changed

src/cdk/collections/data-source.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*/
88

99
import {Observable} from 'rxjs/Observable';
10-
import {of} from 'rxjs/observable/of';
1110
import {CollectionViewer} from './collection-viewer';
1211

1312

@@ -31,15 +30,3 @@ export abstract class DataSource<T> {
3130
*/
3231
abstract disconnect(collectionViewer: CollectionViewer): void;
3332
}
34-
35-
36-
/** DataSource wrapper for a native array. */
37-
export class ArrayDataSource<T> implements DataSource<T> {
38-
constructor(private _data: T[]) {}
39-
40-
connect(): Observable<T[]> {
41-
return of(this._data);
42-
}
43-
44-
disconnect() {}
45-
}

src/cdk/collections/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
export * from './collection-viewer';
1010
export * from './data-source';
1111
export * from './selection';
12+
export * from './static-array-data-source';
1213
export {
1314
UniqueSelectionDispatcher,
1415
UniqueSelectionDispatcherListener,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Observable} from 'rxjs/Observable';
10+
import {of as observableOf} from 'rxjs/observable/of';
11+
import {DataSource} from './data-source';
12+
13+
14+
/** DataSource wrapper for a native array. */
15+
export class StaticArrayDataSource<T> implements DataSource<T> {
16+
constructor(private _data: T[]) {}
17+
18+
connect(): Observable<T[]> {
19+
return observableOf(this._data);
20+
}
21+
22+
disconnect() {}
23+
}

src/cdk/scrolling/public-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
export * from './for-of';
9+
export * from './virtual-for-of';
1010
export * from './scroll-dispatcher';
1111
export * from './scrollable';
1212
export * from './scrolling-module';

src/cdk/scrolling/scrolling-module.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {PlatformModule} from '@angular/cdk/platform';
1010
import {NgModule} from '@angular/core';
11-
import {CdkForOf} from './for-of';
11+
import {CdkVirtualForOf} from './virtual-for-of';
1212
import {SCROLL_DISPATCHER_PROVIDER} from './scroll-dispatcher';
1313
import {CdkScrollable} from './scrollable';
1414
import {CdkVirtualScrollFixedSize} from './virtual-scroll-fixed-size';
@@ -17,13 +17,13 @@ import {CdkVirtualScrollViewport} from './virtual-scroll-viewport';
1717
@NgModule({
1818
imports: [PlatformModule],
1919
exports: [
20-
CdkForOf,
20+
CdkVirtualForOf,
2121
CdkScrollable,
2222
CdkVirtualScrollFixedSize,
2323
CdkVirtualScrollViewport,
2424
],
2525
declarations: [
26-
CdkForOf,
26+
CdkVirtualForOf,
2727
CdkScrollable,
2828
CdkVirtualScrollFixedSize,
2929
CdkVirtualScrollViewport,

src/cdk/scrolling/for-of.ts renamed to src/cdk/scrolling/virtual-for-of.ts

Lines changed: 90 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {ArrayDataSource, CollectionViewer, DataSource, Range} from '@angular/cdk/collections';
9+
import {CollectionViewer, DataSource, Range, StaticArrayDataSource} from '@angular/cdk/collections';
1010
import {
1111
Directive,
1212
DoCheck,
@@ -32,24 +32,22 @@ import {Subject} from 'rxjs/Subject';
3232
import {CdkVirtualScrollViewport} from './virtual-scroll-viewport';
3333

3434

35-
/** The context for an item rendered by `CdkForOf` */
36-
export class CdkForOfContext<T> {
37-
constructor(public $implicit: T, public cdkForOf: NgIterable<T> | DataSource<T>,
38-
public index: number, public count: number) {}
39-
40-
get first(): boolean { return this.index === 0; }
41-
42-
get last(): boolean { return this.index === this.count - 1; }
43-
44-
get even(): boolean { return this.index % 2 === 0; }
45-
46-
get odd(): boolean { return !this.even; }
47-
}
35+
/** The context for an item rendered by `CdkVirtualForOf` */
36+
export type CdkVirtualForOfContext<T> = {
37+
$implicit: T;
38+
cdkVirtualForOf: NgIterable<T> | DataSource<T>;
39+
index: number;
40+
count: number;
41+
first: boolean;
42+
last: boolean;
43+
even: boolean;
44+
odd: boolean;
45+
};
4846

4947

5048
type RecordViewTuple<T> = {
5149
record: IterableChangeRecord<T> | null,
52-
view?: EmbeddedViewRef<CdkForOfContext<T>>
50+
view?: EmbeddedViewRef<CdkVirtualForOfContext<T>>
5351
};
5452

5553

@@ -58,53 +56,63 @@ type RecordViewTuple<T> = {
5856
* container.
5957
*/
6058
@Directive({
61-
selector: '[cdkFor][cdkForOf]',
59+
selector: '[cdkVirtualFor][cdkVirtualForOf]',
6260
})
63-
export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
61+
export class CdkVirtualForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
6462
/** Emits when the rendered view of the data changes. */
6563
viewChange = new Subject<Range>();
6664

67-
/** Emits when the data source changes. */
68-
private _dataSourceSubject = new Subject<DataSource<T>>();
65+
/** Subject that emits when a new DataSource instance is given. */
66+
private _dataSourceChanges = new Subject<DataSource<T>>();
6967

7068
/** The DataSource to display. */
7169
@Input()
72-
get cdkForOf(): NgIterable<T> | DataSource<T> { return this._cdkForOf; }
73-
set cdkForOf(value: NgIterable<T> | DataSource<T>) {
74-
this._cdkForOf = value;
75-
let ds = value instanceof DataSource ? value :
76-
new ArrayDataSource<T>(Array.prototype.slice.call(value));
77-
this._dataSourceSubject.next(ds);
70+
get cdkVirtualForOf(): NgIterable<T> | DataSource<T> { return this._cdkVirtualForOf; }
71+
set cdkVirtualForOf(value: NgIterable<T> | DataSource<T>) {
72+
this._cdkVirtualForOf = value;
73+
const ds = value instanceof DataSource ? value :
74+
// Slice the value since NgIterable may be array-like rather than an array.
75+
new StaticArrayDataSource<T>(Array.prototype.slice.call(value));
76+
this._dataSourceChanges.next(ds);
7877
}
79-
_cdkForOf: NgIterable<T> | DataSource<T>;
78+
_cdkVirtualForOf: NgIterable<T> | DataSource<T>;
8079

81-
/** The trackBy function to use for tracking elements. */
80+
/**
81+
* The `TrackByFunction` to use for tracking changes. The `TrackByFunction` takes the index and
82+
* the item and produces a value to be used as the item's identity when tracking changes.
83+
*/
8284
@Input()
83-
get cdkForTrackBy(): TrackByFunction<T> {
84-
return this._cdkForOfTrackBy;
85+
get cdkVirtualForTrackBy(): TrackByFunction<T> {
86+
return this._cdkVirtualForTrackBy;
8587
}
86-
set cdkForTrackBy(fn: TrackByFunction<T>) {
88+
set cdkVirtualForTrackBy(fn: TrackByFunction<T>) {
8789
this._needsUpdate = true;
88-
this._cdkForOfTrackBy =
90+
this._cdkVirtualForTrackBy =
8991
(index, item) => fn(index + (this._renderedRange ? this._renderedRange.start : 0), item);
9092
}
91-
private _cdkForOfTrackBy: TrackByFunction<T>;
93+
private _cdkVirtualForTrackBy: TrackByFunction<T>;
9294

9395
/** The template used to stamp out new elements. */
9496
@Input()
95-
set cdkForTemplate(value: TemplateRef<CdkForOfContext<T>>) {
97+
set cdkVirtualForTemplate(value: TemplateRef<CdkVirtualForOfContext<T>>) {
9698
if (value) {
9799
this._needsUpdate = true;
98100
this._template = value;
99101
}
100102
}
101103

102104
/** Emits whenever the data in the current DataSource changes. */
103-
dataStream: Observable<T[]> = this._dataSourceSubject
105+
dataStream: Observable<T[]> = this._dataSourceChanges
104106
.pipe(
107+
// Start off with null `DataSource`.
105108
startWith(null!),
109+
// Bundle up the previous and current data sources so we can work with both.
106110
pairwise(),
111+
// Use `_changeDataSource` to disconnect from the previous data source and connect to the
112+
// new one, passing back a stream of data changes which we run through `switchMap` to give
113+
// us a data stream that emits the latest data from whatever the current `DataSource` is.
107114
switchMap(([prev, cur]) => this._changeDataSource(prev, cur)),
115+
// Replay the last emitted data when someone subscribes.
108116
shareReplay(1));
109117

110118
private _differ: IterableDiffer<T> | null = null;
@@ -115,13 +123,13 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
115123

116124
private _renderedRange: Range;
117125

118-
private _templateCache: EmbeddedViewRef<CdkForOfContext<T>>[] = [];
126+
private _templateCache: EmbeddedViewRef<CdkVirtualForOfContext<T>>[] = [];
119127

120128
private _needsUpdate = false;
121129

122130
constructor(
123131
private _viewContainerRef: ViewContainerRef,
124-
private _template: TemplateRef<CdkForOfContext<T>>,
132+
private _template: TemplateRef<CdkVirtualForOfContext<T>>,
125133
private _differs: IterableDiffers,
126134
@Host() private _viewport: CdkVirtualScrollViewport) {
127135
this.dataStream.subscribe(data => this._data = data);
@@ -141,7 +149,8 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
141149
throw Error('Error: attempted to measure an element that isn\'t rendered.');
142150
}
143151
index -= this._renderedRange.start;
144-
let view = this._viewContainerRef.get(index) as EmbeddedViewRef<CdkForOfContext<T>> | null;
152+
let view = this._viewContainerRef.get(index) as
153+
EmbeddedViewRef<CdkVirtualForOfContext<T>> | null;
145154
if (view && view.rootNodes.length) {
146155
let minTop = Infinity;
147156
let minLeft = Infinity;
@@ -172,15 +181,19 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
172181
ngDoCheck() {
173182
if (this._differ && this._needsUpdate) {
174183
const changes = this._differ.diff(this._renderedItems);
175-
this._applyChanges(changes);
184+
if (!changes) {
185+
this._updateContext();
186+
} else {
187+
this._applyChanges(changes);
188+
}
176189
this._needsUpdate = false;
177190
}
178191
}
179192

180193
ngOnDestroy() {
181194
this._viewport.disconnect();
182195

183-
this._dataSourceSubject.complete();
196+
this._dataSourceChanges.complete();
184197
this.viewChange.complete();
185198

186199
for (let view of this._templateCache) {
@@ -194,7 +207,7 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
194207
this.viewChange.next(this._renderedRange);
195208
this._renderedItems = this._data.slice(this._renderedRange.start, this._renderedRange.end);
196209
if (!this._differ) {
197-
this._differ = this._differs.find(this._renderedItems).create(this.cdkForTrackBy);
210+
this._differ = this._differs.find(this._renderedItems).create(this.cdkVirtualForTrackBy);
198211
}
199212
this._needsUpdate = true;
200213
}
@@ -208,24 +221,24 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
208221
return newDs.connect(this);
209222
}
210223

211-
/** Apply changes to the DOM. */
212-
private _applyChanges(changes: IterableChanges<T> | null) {
213-
// If there are no changes, just update the index and count on the view context and be done.
214-
if (!changes) {
215-
for (let i = 0, len = this._viewContainerRef.length; i < len; i++) {
216-
let view = this._viewContainerRef.get(i) as EmbeddedViewRef<CdkForOfContext<T>>;
217-
view.context.index = this._renderedRange.start + i;
218-
view.context.count = this._data.length;
219-
view.detectChanges();
220-
}
221-
return;
224+
/** Update the `CdkVirtualForOfContext` for all views. */
225+
private _updateContext() {
226+
for (let i = 0, len = this._viewContainerRef.length; i < len; i++) {
227+
let view = this._viewContainerRef.get(i) as EmbeddedViewRef<CdkVirtualForOfContext<T>>;
228+
view.context.index = this._renderedRange.start + i;
229+
view.context.count = this._data.length;
230+
this._updateComputedContextProperties(view.context);
231+
view.detectChanges();
222232
}
233+
}
223234

235+
/** Apply changes to the DOM. */
236+
private _applyChanges(changes: IterableChanges<T>) {
224237
// Detach all of the views and add them into an array to preserve their original order.
225-
const previousViews: EmbeddedViewRef<CdkForOfContext<T>>[] = [];
238+
const previousViews: EmbeddedViewRef<CdkVirtualForOfContext<T>>[] = [];
226239
for (let i = 0, len = this._viewContainerRef.length; i < len; i++) {
227240
previousViews.unshift(
228-
this._viewContainerRef.detach()! as EmbeddedViewRef<CdkForOfContext<T>>);
241+
this._viewContainerRef.detach()! as EmbeddedViewRef<CdkVirtualForOfContext<T>>);
229242
}
230243

231244
// Mark the removed indices so we can recycle their views.
@@ -255,21 +268,43 @@ export class CdkForOf<T> implements CollectionViewer, DoCheck, OnDestroy {
255268
}
256269

257270
// We now have a full list of everything to be inserted, so go ahead and insert them.
271+
this._insertViews(insertTuples);
272+
}
273+
274+
/** Insert the RecordViewTuples into the container element. */
275+
private _insertViews(insertTuples: RecordViewTuple<T>[]) {
258276
for (let i = 0, len = insertTuples.length; i < len; i++) {
259277
let {view, record} = insertTuples[i];
260278
if (view) {
261279
this._viewContainerRef.insert(view);
262280
} else {
263-
view = this._viewContainerRef.createEmbeddedView(this._template,
264-
new CdkForOfContext<T>(null!, this._cdkForOf, -1, -1));
281+
view = this._viewContainerRef.createEmbeddedView(this._template, {
282+
$implicit: null!,
283+
cdkVirtualForOf: this._cdkVirtualForOf,
284+
index: -1,
285+
count: -1,
286+
first: false,
287+
last: false,
288+
odd: false,
289+
even: false
290+
});
265291
}
266292

267293
if (record) {
268294
view.context.$implicit = record.item as T;
269295
}
270296
view.context.index = this._renderedRange.start + i;
271297
view.context.count = this._data.length;
298+
this._updateComputedContextProperties(view.context);
272299
view.detectChanges();
273300
}
274301
}
302+
303+
/** Update the computed properties on the `CdkVirtualForOfContext`. */
304+
private _updateComputedContextProperties(context: CdkVirtualForOfContext<any>) {
305+
context.first = context.index === 0;
306+
context.last = context.index === context.count - 1;
307+
context.even = context.index % 2 === 0;
308+
context.odd = !context.even;
309+
}
275310
}

0 commit comments

Comments
 (0)