@@ -6,6 +6,10 @@ import {
66 ContentChildren ,
77 Directive ,
88 Input ,
9+ IterableChangeRecord ,
10+ IterableDiffer ,
11+ IterableDiffers ,
12+ NgIterable ,
913 QueryList ,
1014 ViewChild ,
1115 ViewContainerRef ,
@@ -38,7 +42,7 @@ export class HeaderRowPlaceholder {
3842}
3943
4044/**
41- * A data table that connects with a data source to retrieve data and renders
45+ * A data table that connects with a data source to retrieve data of type T and renders
4246 * a header row and data rows. Updates the rows when new data is provided by the data source.
4347 */
4448@Component ( {
@@ -54,12 +58,12 @@ export class HeaderRowPlaceholder {
5458 encapsulation : ViewEncapsulation . None ,
5559 changeDetection : ChangeDetectionStrategy . OnPush ,
5660} )
57- export class CdkTable implements CollectionViewer {
61+ export class CdkTable < T > implements CollectionViewer {
5862 /**
5963 * Provides a stream containing the latest data array to render. Influenced by the table's
6064 * stream of view window (what rows are currently on screen).
6165 */
62- @Input ( ) dataSource : DataSource < any > ;
66+ @Input ( ) dataSource : DataSource < T > ;
6367
6468 // TODO(andrewseguin): Remove max value as the end index
6569 // and instead calculate the view on init and scroll.
@@ -76,6 +80,9 @@ export class CdkTable implements CollectionViewer {
7680 */
7781 private _columnDefinitionsByName = new Map < string , CdkColumnDef > ( ) ;
7882
83+ /** Differ used to find the changes in the data provided by the data source. */
84+ private _dataDiffer : IterableDiffer < T > = null ;
85+
7986 // Placeholders within the table's template where the header and data rows will be inserted.
8087 @ViewChild ( RowPlaceholder ) _rowPlaceholder : RowPlaceholder ;
8188 @ViewChild ( HeaderRowPlaceholder ) _headerRowPlaceholder : HeaderRowPlaceholder ;
@@ -92,9 +99,14 @@ export class CdkTable implements CollectionViewer {
9299 /** Set of templates that used as the data row containers. */
93100 @ContentChildren ( CdkRowDef ) _rowDefinitions : QueryList < CdkRowDef > ;
94101
95- constructor ( private _changeDetectorRef : ChangeDetectorRef ) {
102+ constructor ( private readonly _differs : IterableDiffers ,
103+ private readonly _changeDetectorRef : ChangeDetectorRef ) {
96104 console . warn ( 'The data table is still in active development ' +
97105 'and should be considered unstable.' ) ;
106+
107+ // TODO(andrewseguin): Add trackby function input.
108+ // Find and construct an iterable differ that can be used to find the diff in an array.
109+ this . _dataDiffer = this . _differs . find ( [ ] ) . create ( ) ;
98110 }
99111
100112 ngOnDestroy ( ) {
@@ -122,12 +134,8 @@ export class CdkTable implements CollectionViewer {
122134 // TODO(andrewseguin): If the data source is not
123135 // present after view init, connect it when it is defined.
124136 // TODO(andrewseguin): Unsubscribe from this on destroy.
125- this . dataSource . connect ( this ) . subscribe ( ( rowsData : any [ ] ) => {
126- // TODO(andrewseguin): Add a differ that will check if the data has changed,
127- // rather than re-rendering all rows
128- this . _rowPlaceholder . viewContainer . clear ( ) ;
129- rowsData . forEach ( rowData => this . insertRow ( rowData ) ) ;
130- this . _changeDetectorRef . markForCheck ( ) ;
137+ this . dataSource . connect ( this ) . subscribe ( ( rowsData : NgIterable < T > ) => {
138+ this . renderRowChanges ( rowsData ) ;
131139 } ) ;
132140 }
133141
@@ -146,11 +154,31 @@ export class CdkTable implements CollectionViewer {
146154 CdkCellOutlet . mostRecentCellOutlet . context = { } ;
147155 }
148156
157+ /** Check for changes made in the data and render each change (row added/removed/moved). */
158+ renderRowChanges ( dataRows : NgIterable < T > ) {
159+ const changes = this . _dataDiffer . diff ( dataRows ) ;
160+ if ( ! changes ) { return ; }
161+
162+ changes . forEachOperation (
163+ ( item : IterableChangeRecord < any > , adjustedPreviousIndex : number , currentIndex : number ) => {
164+ if ( item . previousIndex == null ) {
165+ this . insertRow ( dataRows [ currentIndex ] , currentIndex ) ;
166+ } else if ( currentIndex == null ) {
167+ this . _rowPlaceholder . viewContainer . remove ( adjustedPreviousIndex ) ;
168+ } else {
169+ const view = this . _rowPlaceholder . viewContainer . get ( adjustedPreviousIndex ) ;
170+ this . _rowPlaceholder . viewContainer . move ( view , currentIndex ) ;
171+ }
172+ } ) ;
173+
174+ this . _changeDetectorRef . markForCheck ( ) ;
175+ }
176+
149177 /**
150178 * Create the embedded view for the data row template and place it in the correct index location
151179 * within the data row view container.
152180 */
153- insertRow ( rowData : any ) {
181+ insertRow ( rowData : T , index : number ) {
154182 // TODO(andrewseguin): Add when predicates to the row definitions
155183 // to find the right template to used based on
156184 // the data rather than choosing the first row definition.
@@ -161,7 +189,7 @@ export class CdkTable implements CollectionViewer {
161189
162190 // TODO(andrewseguin): add some code to enforce that exactly one
163191 // CdkCellOutlet was instantiated as a result of `createEmbeddedView`.
164- this . _rowPlaceholder . viewContainer . createEmbeddedView ( row . template , context ) ;
192+ this . _rowPlaceholder . viewContainer . createEmbeddedView ( row . template , context , index ) ;
165193
166194 // Insert empty cells if there is no data to improve rendering time.
167195 CdkCellOutlet . mostRecentCellOutlet . cells = rowData ? this . getCellTemplatesForRow ( row ) : [ ] ;
0 commit comments