Skip to content

Commit 6d36942

Browse files
authored
fix(material/table): unsubscribe from source when disconnecting (#21338)
* Fixes that the table data source wasn't unsubscribing from sorting, pagination etc. when it is disconnected. * Adds a bit of logic to properly support re-connecting the same date source again. * Fixes up a couple of issues in the unit test setup which could cause state to leak between tests. Fixes #21270.
1 parent 411b174 commit 6d36942

File tree

3 files changed

+31
-8
lines changed

3 files changed

+31
-8
lines changed

src/material/table/table-data-source.spec.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations';
55
import {Component, ViewChild} from '@angular/core';
66

77
describe('MatTableDataSource', () => {
8-
const dataSource = new MatTableDataSource();
9-
108
beforeEach(waitForAsync(() => {
119
TestBed.configureTestingModule({
1210
imports: [MatSortModule, NoopAnimationsModule],
@@ -15,13 +13,16 @@ describe('MatTableDataSource', () => {
1513
}));
1614

1715
describe('sort', () => {
16+
let dataSource: MatTableDataSource<any>;
1817
let fixture: ComponentFixture<MatSortApp>;
1918
let sort: MatSort;
2019

2120
beforeEach(() => {
2221
fixture = TestBed.createComponent(MatSortApp);
2322
fixture.detectChanges();
23+
dataSource = new MatTableDataSource();
2424
sort = fixture.componentInstance.sort;
25+
dataSource.sort = sort;
2526
});
2627

2728
/** Test the data source's `sortData` function. */
@@ -51,6 +52,19 @@ describe('MatTableDataSource', () => {
5152
it('should be able to correctly sort an array of strings and numbers', () => {
5253
testSortWithValues([3, 'apples', 'bananas', 'cherries', 'lemons', 'strawberries']);
5354
});
55+
56+
it('should unsubscribe from the re-render stream when disconnected', () => {
57+
const spy = spyOn(dataSource._renderChangesSubscription!, 'unsubscribe');
58+
dataSource.disconnect();
59+
expect(spy).toHaveBeenCalledTimes(1);
60+
});
61+
62+
it('should re-subscribe to the sort stream when re-connecting after being disconnected', () => {
63+
dataSource.disconnect();
64+
const spy = spyOn(fixture.componentInstance.sort.sortChange, 'subscribe');
65+
dataSource.connect();
66+
expect(spy).toHaveBeenCalledTimes(1);
67+
});
5468
});
5569
});
5670

src/material/table/table-data-source.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class _MatTableDataSource<T, P extends Paginator> extends DataSource<T> {
5353
* Subscription to the changes that should trigger an update to the table's rendered rows, such
5454
* as filtering, sorting, pagination, or base data changes.
5555
*/
56-
_renderChangesSubscription = Subscription.EMPTY;
56+
_renderChangesSubscription: Subscription|null = null;
5757

5858
/**
5959
* The filtered set of data that has been matched by the filter string, or all the data if there
@@ -244,7 +244,7 @@ export class _MatTableDataSource<T, P extends Paginator> extends DataSource<T> {
244244
const paginatedData = combineLatest([orderedData, pageChange])
245245
.pipe(map(([data]) => this._pageData(data)));
246246
// Watched for paged data changes and send the result to the table to render.
247-
this._renderChangesSubscription.unsubscribe();
247+
this._renderChangesSubscription?.unsubscribe();
248248
this._renderChangesSubscription = paginatedData.subscribe(data => this._renderData.next(data));
249249
}
250250

@@ -321,13 +321,22 @@ export class _MatTableDataSource<T, P extends Paginator> extends DataSource<T> {
321321
* Used by the MatTable. Called when it connects to the data source.
322322
* @docs-private
323323
*/
324-
connect() { return this._renderData; }
324+
connect() {
325+
if (!this._renderChangesSubscription) {
326+
this._updateChangeSubscription();
327+
}
328+
329+
return this._renderData;
330+
}
325331

326332
/**
327-
* Used by the MatTable. Called when it is destroyed. No-op.
333+
* Used by the MatTable. Called when it disconnects from the data source.
328334
* @docs-private
329335
*/
330-
disconnect() { }
336+
disconnect() {
337+
this._renderChangesSubscription?.unsubscribe();
338+
this._renderChangesSubscription = null;
339+
}
331340
}
332341

333342
/**

tools/public_api_guard/material/table.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export declare const _MAT_TEXT_COLUMN_TEMPLATE = "\n <ng-container matColumnDef>\n <th mat-header-cell *matHeaderCellDef [style.text-align]=\"justify\">\n {{headerText}}\n </th>\n <td mat-cell *matCellDef=\"let data\" [style.text-align]=\"justify\">\n {{dataAccessor(data, name)}}\n </td>\n </ng-container>\n";
22

33
export declare class _MatTableDataSource<T, P extends Paginator> extends DataSource<T> {
4-
_renderChangesSubscription: Subscription;
4+
_renderChangesSubscription: Subscription | null;
55
get data(): T[];
66
set data(data: T[]);
77
get filter(): string;

0 commit comments

Comments
 (0)