0

My issue is similar to the one discussed here, but none of the suggestions there has helped, and my situation is slightly different.

I have a parent component assigning an array to a child component's input property, which then uses that data to create a data source for a mat table. My code is similar to this:

parent.component.html:

<mat-tab label="Accounts">
  <app-accounts [accounts]="accountOwner?.accounts"></app-accounts>
</mat-tab>

child.component.ts:

@Component({ /// })
  export class AppAccountComponent implements OnInit, OnChanges {
  @Input() accounts: MyAccount[];
tableColumns: string[] = ['name', 'number', 'startDate', 'endDate'];
accountDataSource: MatTableDataSource<MyAccount>;

@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;

constructor() {
  // tried this as recommended in linked thread, but it had no effect:
  // this.accountDataSource = new MatTableDataSource<MyAccount>();
}

ngOnInit(): void {
  this.sort.active = 'name';
  this.sort.direction = 'asc';
}

ngOnChanges(changes: SimpleChanges): void {
  this.accountDataSource = new MatTableDataSource<MyAccount>(this.accounts);
  this.accountDataSource.sort = this.sort;
  this.accountDataSource.paginator = this.paginator;
}
}

child.component.html:

<table mat-table [dataSource]="accountsDataSource" class="mat-elevation-z1" matSort>
  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
    <td mat-cell *matCellDef="let account"> {{account.name}} </td>
  </ng-container>

  <ng-container matColumnDef="number">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Number </th>
    <td mat-cell *matCellDef="let account"> {{account.number}} </td>
  </ng-container>

  <ng-container matColumnDef="startDate">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Effective Date </th>
    <td mat-cell *matCellDef="let account"> {{account.startDate | date:'mediumDate'}} </td>
  </ng-container>

  <ng-container matColumnDef="endDate">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Expiration Date </th>
    <td mat-cell *matCellDef="let account"> {{account.endDate | date:'mediumDate'}} </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="tableColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: tableColumns;"></tr>
</table>
<mat-paginator [pageSizeOptions]="[5, 10, 20]" [disabled]="!accountsDataSource" [pageSize]="5" showFirstLastButtons>
</mat-paginator>

The MyAccounts interface:

export interface MyAccount {
  number: string;
  name: string;
  startDate: Date;
  endDate?: Date;
}

This actually displays the data correctly. The problem is this exception in the console window:

enter image description here

This "property length of null" exception looks like it's coming from MatTableDataSource._filterData().

Syntle
  • 5,168
  • 3
  • 13
  • 34
RobC
  • 1,303
  • 3
  • 15
  • 32
  • Your account input can be null(not really but your input wont receive a value): accountOwner?.accounts. It triggers the ngOnChanges which makes you build a dataSource with a possible null value. Try initializing your input to see if the error goes away:@Input() accounts: MyAccount[] = new Array(); – ukn Jun 16 '20 at 15:58
  • @ukn I tried your suggestion, but I'm still getting the same exception. Thanks, though. – RobC Jun 16 '20 at 17:13
  • 1
    You should modify your ngOnChanges, updating the dataSource everytime an input changes is not a good idea. Check if account changed using current and previous that SimpleChanges provide you. If you want to update the data then update just the data without creating a new datasource, unless it doesnt exist yet. The last thing is the ternary(from your answer) shouldnt be needed if the rest of what I said was implemented. – ukn Jun 16 '20 at 19:24
  • @ukn Why is updating the data source when the input changes a bad idea? Is it expensive? – RobC Jun 17 '20 at 18:51
  • 1
    Updating is fine, creating a new one is not. Instead of creating the Datasource, just update the data. this.accountDataSource.data = yourData; – ukn Jun 17 '20 at 18:54

1 Answers1

0

I figured this out and am embarrassed by the answer.

The first (but not last) time ngOnChanges gets hit, the array is null, but on subsequent times it's not. So the following fixed things:

this.accountDataSource = new MatTableDataSource<MyAccount>(this.accounts ? this.accounts : []);
RobC
  • 1,303
  • 3
  • 15
  • 32