From 6f7f9f45f0fcf19cc278b7aabc1e11fceef99a8e Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sun, 4 Apr 2021 19:03:09 +0200 Subject: [PATCH] fix(material/table): better handling of invalid data The table data source is set up to expect an array and will throw a cryptic error down the line if the value is anything different. While typings should be enough to enforce this, if the value comes from somewhere in the view it may not get caught. Since the effort for handling it on our end is minimal, these changes add some logic that fall back to an empty array if the value is invalid. Fixes #18859. --- .../mdc-table/table.spec.ts | 39 +++++++++++++++++++ src/material/table/table-data-source.ts | 1 + src/material/table/table.spec.ts | 39 +++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/src/material-experimental/mdc-table/table.spec.ts b/src/material-experimental/mdc-table/table.spec.ts index 08d9f00adcee..471562196177 100644 --- a/src/material-experimental/mdc-table/table.spec.ts +++ b/src/material-experimental/mdc-table/table.spec.ts @@ -591,6 +591,45 @@ describe('MDC-based MatTable', () => { ['Footer A', 'Footer B', 'Footer C'], ]); }); + + it('should fall back to empty table if invalid data is passed in', () => { + component.underlyingDataSource.addData(); + fixture.detectChanges(); + expectTableToMatchContent(tableElement, [ + ['Column A', 'Column B', 'Column C'], + ['a_1', 'b_1', 'c_1'], + ['a_2', 'b_2', 'c_2'], + ['a_3', 'b_3', 'c_3'], + ['a_4', 'b_4', 'c_4'], + ['Footer A', 'Footer B', 'Footer C'], + ]); + + dataSource.data = null!; + fixture.detectChanges(); + expectTableToMatchContent(tableElement, [ + ['Column A', 'Column B', 'Column C'], + ['Footer A', 'Footer B', 'Footer C'], + ]); + + component.underlyingDataSource.addData(); + fixture.detectChanges(); + expectTableToMatchContent(tableElement, [ + ['Column A', 'Column B', 'Column C'], + ['a_1', 'b_1', 'c_1'], + ['a_2', 'b_2', 'c_2'], + ['a_3', 'b_3', 'c_3'], + ['a_4', 'b_4', 'c_4'], + ['a_5', 'b_5', 'c_5'], + ['Footer A', 'Footer B', 'Footer C'], + ]); + + dataSource.data = {} as any; + fixture.detectChanges(); + expectTableToMatchContent(tableElement, [ + ['Column A', 'Column B', 'Column C'], + ['Footer A', 'Footer B', 'Footer C'], + ]); + }); }); }); diff --git a/src/material/table/table-data-source.ts b/src/material/table/table-data-source.ts index 22d01b86e661..119968bab0ac 100644 --- a/src/material/table/table-data-source.ts +++ b/src/material/table/table-data-source.ts @@ -85,6 +85,7 @@ export class _MatTableDataSource< return this._data.value; } set data(data: T[]) { + data = Array.isArray(data) ? data : []; this._data.next(data); // Normally the `filteredData` is updated by the re-render // subscription, but that won't happen if it's inactive. diff --git a/src/material/table/table.spec.ts b/src/material/table/table.spec.ts index e47337e73a68..0b5d0cb14069 100644 --- a/src/material/table/table.spec.ts +++ b/src/material/table/table.spec.ts @@ -576,6 +576,45 @@ describe('MatTable', () => { ['Footer A', 'Footer B', 'Footer C'], ]); }); + + it('should fall back to empty table if invalid data is passed in', () => { + component.underlyingDataSource.addData(); + fixture.detectChanges(); + expectTableToMatchContent(tableElement, [ + ['Column A', 'Column B', 'Column C'], + ['a_1', 'b_1', 'c_1'], + ['a_2', 'b_2', 'c_2'], + ['a_3', 'b_3', 'c_3'], + ['a_4', 'b_4', 'c_4'], + ['Footer A', 'Footer B', 'Footer C'], + ]); + + dataSource.data = null!; + fixture.detectChanges(); + expectTableToMatchContent(tableElement, [ + ['Column A', 'Column B', 'Column C'], + ['Footer A', 'Footer B', 'Footer C'], + ]); + + component.underlyingDataSource.addData(); + fixture.detectChanges(); + expectTableToMatchContent(tableElement, [ + ['Column A', 'Column B', 'Column C'], + ['a_1', 'b_1', 'c_1'], + ['a_2', 'b_2', 'c_2'], + ['a_3', 'b_3', 'c_3'], + ['a_4', 'b_4', 'c_4'], + ['a_5', 'b_5', 'c_5'], + ['Footer A', 'Footer B', 'Footer C'], + ]); + + dataSource.data = {} as any; + fixture.detectChanges(); + expectTableToMatchContent(tableElement, [ + ['Column A', 'Column B', 'Column C'], + ['Footer A', 'Footer B', 'Footer C'], + ]); + }); }); });