Skip to content

Commit 846f5a2

Browse files
crisbetoVivian Hu
authored and
Vivian Hu
committedJan 18, 2019
fix(table): not re-rendering when switching to a smaller set of data than the current page (#14665)
Fixes the table not rendering correctly when going from a large set of data (e.g. 50 items and on page 10) to a small set of items (e.g. 1 item). The issue comes from the fact that the paginator doesn't emit events if they weren't generated by the user (see discussion on #12586). Fixes #14010.
1 parent 961d8bc commit 846f5a2

File tree

2 files changed

+56
-15
lines changed

2 files changed

+56
-15
lines changed
 

‎src/lib/table/table-data-source.ts

+22-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {
1414
merge,
1515
Observable,
1616
of as observableOf,
17-
Subscription
17+
Subscription,
18+
Subject,
1819
} from 'rxjs';
1920
import {MatPaginator, PageEvent} from '@angular/material/paginator';
2021
import {MatSort, Sort} from '@angular/material/sort';
@@ -44,6 +45,9 @@ export class MatTableDataSource<T> extends DataSource<T> {
4445
/** Stream that emits when a new filter string is set on the data source. */
4546
private readonly _filter = new BehaviorSubject<string>('');
4647

48+
/** Used to react to internal changes of the paginator that are made by the data source itself. */
49+
private readonly _internalPageChanges = new Subject<void>();
50+
4751
/**
4852
* Subscription to the changes that should trigger an update to the table's rendered rows, such
4953
* as filtering, sorting, pagination, or base data changes.
@@ -211,9 +215,9 @@ export class MatTableDataSource<T> extends DataSource<T> {
211215
merge<Sort|void>(this._sort.sortChange, this._sort.initialized) :
212216
observableOf(null);
213217
const pageChange: Observable<PageEvent|null|void> = this._paginator ?
214-
merge<PageEvent|void>(this._paginator.page, this._paginator.initialized) :
218+
merge<PageEvent|void>(
219+
this._paginator.page, this._internalPageChanges, this._paginator.initialized) :
215220
observableOf(null);
216-
217221
const dataStream = this._data;
218222
// Watch for base data or filter changes to provide a filtered set of data.
219223
const filteredData = combineLatest(dataStream, this._filter)
@@ -276,14 +280,24 @@ export class MatTableDataSource<T> extends DataSource<T> {
276280
*/
277281
_updatePaginator(filteredDataLength: number) {
278282
Promise.resolve().then(() => {
279-
if (!this.paginator) { return; }
283+
const paginator = this.paginator;
284+
285+
if (!paginator) { return; }
280286

281-
this.paginator.length = filteredDataLength;
287+
paginator.length = filteredDataLength;
282288

283289
// If the page index is set beyond the page, reduce it to the last page.
284-
if (this.paginator.pageIndex > 0) {
285-
const lastPageIndex = Math.ceil(this.paginator.length / this.paginator.pageSize) - 1 || 0;
286-
this.paginator.pageIndex = Math.min(this.paginator.pageIndex, lastPageIndex);
290+
if (paginator.pageIndex > 0) {
291+
const lastPageIndex = Math.ceil(paginator.length / paginator.pageSize) - 1 || 0;
292+
const newPageIndex = Math.min(paginator.pageIndex, lastPageIndex);
293+
294+
if (newPageIndex !== paginator.pageIndex) {
295+
paginator.pageIndex = newPageIndex;
296+
297+
// Since the paginator only emits after user-generated changes,
298+
// we need our own stream so we know to should re-render the data.
299+
this._internalPageChanges.next();
300+
}
287301
}
288302
});
289303
}

‎src/lib/table/table.spec.ts

+34-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {DataSource} from '@angular/cdk/collections';
2-
import {Component, OnInit, ViewChild} from '@angular/core';
2+
import {Component, OnInit, ViewChild, AfterViewInit} from '@angular/core';
33
import {
44
async,
55
ComponentFixture,
@@ -151,14 +151,14 @@ describe('MatTable', () => {
151151
let dataSource: MatTableDataSource<TestData>;
152152
let component: ArrayDataSourceMatTableApp;
153153

154-
beforeEach(() => {
154+
beforeEach(fakeAsync(() => {
155155
fixture = TestBed.createComponent(ArrayDataSourceMatTableApp);
156156
fixture.detectChanges();
157157

158158
tableElement = fixture.nativeElement.querySelector('.mat-table');
159159
component = fixture.componentInstance;
160160
dataSource = fixture.componentInstance.dataSource;
161-
});
161+
}));
162162

163163
it('should create table and display data source contents', () => {
164164
expectTableToMatchContent(tableElement, [
@@ -197,6 +197,33 @@ describe('MatTable', () => {
197197
]);
198198
});
199199

200+
it('should update the page index when switching to a smaller data set from a page',
201+
fakeAsync(() => {
202+
// Add 20 rows so we can switch pages.
203+
for (let i = 0; i < 20; i++) {
204+
component.underlyingDataSource.addData();
205+
fixture.detectChanges();
206+
tick();
207+
fixture.detectChanges();
208+
}
209+
210+
// Go to the last page.
211+
fixture.componentInstance.paginator.lastPage();
212+
fixture.detectChanges();
213+
214+
// Switch to a smaller data set.
215+
dataSource.data = [{a: 'a_0', b: 'b_0', c: 'c_0'}];
216+
fixture.detectChanges();
217+
tick();
218+
fixture.detectChanges();
219+
220+
expectTableToMatchContent(tableElement, [
221+
['Column A', 'Column B', 'Column C'],
222+
['a_0', 'b_0', 'c_0'],
223+
['Footer A', 'Footer B', 'Footer C'],
224+
]);
225+
}));
226+
200227
it('should be able to filter the table contents', fakeAsync(() => {
201228
// Change filter to a_1, should match one row
202229
dataSource.filter = 'a_1';
@@ -650,7 +677,7 @@ class MatTableWithWhenRowApp {
650677
<mat-paginator [pageSize]="5"></mat-paginator>
651678
`
652679
})
653-
class ArrayDataSourceMatTableApp implements OnInit {
680+
class ArrayDataSourceMatTableApp implements AfterViewInit {
654681
underlyingDataSource = new FakeDataSource();
655682
dataSource = new MatTableDataSource<TestData>();
656683
columnsToRender = ['column_a', 'column_b', 'column_c'];
@@ -673,9 +700,9 @@ class ArrayDataSourceMatTableApp implements OnInit {
673700
});
674701
}
675702

676-
ngOnInit() {
677-
this.dataSource!.sort = this.sort;
678-
this.dataSource!.paginator = this.paginator;
703+
ngAfterViewInit() {
704+
this.dataSource.sort = this.sort;
705+
this.dataSource.paginator = this.paginator;
679706
}
680707
}
681708

0 commit comments

Comments
 (0)
Please sign in to comment.