У меня есть angular mat-таблица и кнопка для добавления или удаления столбца. У меня проблема в том, что только при удалении я получаю эту ошибку: ОШИБКА TypeError: Невозможно прочитать свойство 'columnDef' из неопределенного,
родительский компонент просто общается с API и возвращает данные во входные переменные.
прошел и не может найти данные, которые не определены, поэтому это должен быть какой-то жизненный цикл, чего я пока не понимаю , Любые идеи приветствуются.
import { Component, Inject, Input, OnDestroy, OnInit, ViewChild, Output, EventEmitter, OnChanges } from '@angular/core';
import { MatPaginator, MatSort, MatTableDataSource } from '@angular/material';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import paginationDefaults from '../../constants/pagination.config';
import { ConfigureQueryComponent } from '../configure-query/configure-query.component';
import { ColumnBuilder } from './column-builder';
import { ColumnModel } from 'src/app/models/table-definition.model';
@Component({
selector: 'app-data-table',
templateUrl: './data-table.component.html',
styleUrls: ['./data-table.component.scss']
})
export class DataTableComponent implements OnDestroy, OnChanges {
@Output() pageEvent = new EventEmitter<any>();
@Output() sortEvent = new EventEmitter<any>();
@Input() data: any[] = [];
@Input() metaData: any = {};
@Input() type: string;
@Input() selectable: boolean;
@Input() allColumns: any[] = [];
@Input() availableAggregates: string[] = [];
@ViewChild(MatSort, {static: false}) sort: MatSort;
initialized = false;
loading = true;
subscriptions: Subscription[] = [];
dataSource = new MatTableDataSource<any>();
pageProps = paginationDefaults;
paginator: MatPaginator;
_allColumns: any[] = [];
availableColumns: any[] = [];
displayedColumns: string[] = [];
factory = {
sortActive: '',
sortDirection: 'asc',
columns: []
};
/**
* @param route used to build the params
* @param dataService the service for the api endpoints
* @param router used to navigate to individual detail views (future implementation)
* @param dialog Material Dialog to open the Configure Columns component
* @param columnBuilder contains the logic to build and change the table columns
*/
constructor(
private route: ActivatedRoute,
private router: Router,
public dialog: MatDialog,
@Inject(ColumnBuilder) public columnBuilder: ColumnBuilder
) {}
/**
* @description - subscribes to the dataService method to call updateData with the api result when it's returned.
* also subscribes to the subject in the columnBuilder class for when the columns are updated.
* put both subscriptions into an array to make managing easier.
*/
ngOnChanges(): void {
console.warn('ngOnChanges');
if (!this.initialized && this.allColumns.length > 0 && this.availableAggregates.length > 0) {
this._allColumns = this.allColumns;
this.initialized = true;
this.manageSubscriptions();
}
if (this.initialized && this.data) {
this.updateData();
}
}
/**
* @description unsubscribes to all the subscriptions on route change
*/
ngOnDestroy(): void {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
/**
* @description - manages two subscriptions, one for listening to when data is returned and the one for listening to the column
* builder class for when the displayed columns, all columns, and factory columns have been updated. Pushes these to a subscriptions
* array to unsubscribe to all them easier when the route is changed.
*/
manageSubscriptions() {
this.subscriptions.forEach(sub => sub.unsubscribe());
const columnBuilderSubscription = this.columnBuilder.columnsUpdatedSubject.subscribe((data: any) => {
this.displayedColumns = data.displayedColumns;
this._allColumns = data.availableColumns;
this.factory.columns = data.factoryColumns;
console.warn('columnBuilderSubscription', this.displayedColumns, this.factory.columns);
this.buildTable();
});
this.subscriptions.push(columnBuilderSubscription);
}
/**
* @description updates the data and builds columns from the service result.
* @param res an object of the service call results, including the query params.
*/
updateData(): void {
this.pageProps.currentPage = this.route.snapshot.queryParams.page;
if (this.metaData.count) {
this.pageProps.totalRecords = this.metaData.count;
}
this.buildColumns();
}
/**
* @description calls the setColumns function in the columnBuilder class and passes through the data and type.
*/
buildColumns(): void {
/**
* @todo use this function to trigger building the table and decprecate the behavioourSubject description
*/
this.columnBuilder.setColumns(this.data, this.type, this._allColumns);
}
/**
* @description shortens the column header text to format nicely in the table
*/
truncateText(input: string): string {
if (input.length > 20) {
return input.substring(0, 20) + '...';
} else {
return input;
}
}
public setLoadingState(val: boolean) {
this.loading = val;
}
/**
* @description - opens the config query pop up and passes through all columns and the available aggregates. On close it
* builds the columns again.
*/
openConfig() {
const dialogRef = this.dialog.open(ConfigureQueryComponent, {
id: 'QueryConfigDialog',
width: '95%',
data: {
context: this.type,
columns: this._allColumns,
aggregates: this.availableAggregates
}
});
dialogRef.afterClosed().subscribe(() => {
this.availableColumns = JSON.parse(localStorage.getItem('COL_CONFIG')) || false;
this.buildColumns();
});
}
/**
* @description creates a new dataSource for the table
*/
buildTable(): void {
this.dataSource = new MatTableDataSource<any>(this.data);
this.dataSource.sort = this.sort;
}
/**
* @description changes the route to navigate to the individual detail page
* @param index row data uuid number
*/
public getDetails(index: number): void {
this.router.navigate([this.data[index].url]);
}
/**
* @description handles the page event on pagination change
* @param e material pagination details
*/
public handlePage = (e: any) => {
this.loading = true;
this.pageEvent.emit({
page: e.pageIndex,
limit: e.pageSize
});
}
public handleSortBy(e: any): void {
this.loading = true;
this.sortEvent.emit({
sort: e.active,
direction: e.direction,
page: 0
});
this.factory.sortActive = e.active,
this.factory.sortDirection = e.direction;
}
/**
* @description if no data is returned from the dataService, return true.
*/
showNoResults(): boolean {
if (this.data === undefined) {
return true;
}
}
setDefaultColumns(columnData: ColumnModel[]) {
const defaultColumnAmount = 8;
const columns = this.filterOnlyStatic(columnData).splice(0, defaultColumnAmount);
columns.forEach((c) => {
c.show = true;
c.index = columns.indexOf(c);
});
return columns;
}
filterOnlyStatic(columns: ColumnModel[]) {
return columns.filter((col) => col.columnType === 'STATIC' && !col.columnDef.includes('DEVICE'));
}
}