问题
UPDATED: Changed the code to immediately call and assign the dataSource, but I still experience the same results, with the exception that the sourceData.paginator is working. The sourceData.filter and sourceData.sort is still not working.
PLEASE NOTE: I am getting no errors, as the error had to do with the paginator that is now working. Also, it might have to do with the format of my data, so included some data as an example. As a note, I use response.data for assigning my data to the source, and then use {{asset.Data.FleetNumber}} for displaying a field.
I am quite new to Angular 5 and Angular Material. I am struggling with implementing the mat-table dataSource paginator and sort and filterValue.
I get the following Error: (RESOLVED)
TypeError: Cannot set property 'paginator' of undefined
And my filter is not working. When I type in my filter field, after the 2nd character, my list returns empty.
I have tried different life-cycle hooks in an attempt to correctly assigning the sort and paginator to my dataSource, but it does not seem to work: ngDoCheck(), ngAfterViewInit(), ngAfterViewChecked(), ngAfterContentInit(). Obviously some of these cause an infinite loop.
I have implemented my dataSource and paginator and sort and filterValue as follows in my component:
...
dataSource = new MatTableDataSource<Asset>();
displayedColumns = ['fleetNumber', 'make'];
constructor(private assetDataService: AssetDataService, private router: Router,
public dialog: MatDialog, public snackBar: MatSnackBar, private route: ActivatedRoute,
private toastr: ToastrService) {
assetDataService.getAssetList().subscribe(results => {
if (!results) { return };
this.dataSource.data = results;
});
}
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
applyFilter(filterValue: string) {
filterValue = filterValue.trim();
filterValue = filterValue.toLowerCase();
this.dataSource.filter = filterValue;
}
...
My view:
<div class="asset-container">
<div class="asset-header">
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
</div>
<mat-table #table [dataSource]="dataSource" matSort>
<!-- Id Column -->
<ng-container matColumnDef="fleetNumber">
<mat-header-cell *matHeaderCellDef mat-sort-header> Fleet Number </mat-header-cell>
<mat-cell *matCellDef="let asset"> {{asset.Data.FleetNumber}} </mat-cell>
</ng-container>
<!-- Make Column -->
<ng-container matColumnDef="make">
<mat-header-cell *matHeaderCellDef mat-sort-header> Make </mat-header-cell>
<mat-cell *matCellDef="let asset"> {{asset.Data.Make}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
<mat-paginator [pageSizeOptions]="[5, 10, 25, 100]"></mat-paginator>
</div>
Sample of data (one object in my array of objects returned. I use response.data for assigning my data to the source, and then use {{asset.Data.FleetNumber}} for displaying a field:
[ {
"Id":"5ee436a4-55ce-47b5-8489-82b190e7d489",
"AssetType":"c00a71f5-d7f6-403b-b13f-37cf35961456",
"AssetDetailId":"716ade20-871b-495b-9bb7-081d7cf1e57d",
"Name":"Vehicle",
"Description":"Vehicle Description",
"TotalQuantity":1,
"AssetDetail":{
"Id":"716ade20-871b-495b-9bb7-081d7cf1e57d",
"Data":null,
"Created":"2018-02-26T06:05:03.0695992Z",
"CreatedById":null
},
"IncludeData":false,
"Data":{
"Id":null,
"FleetNumber":"FL002",
"VehicleRegistrationNumber":"HFJ001GP",
"LicenceNumber":"L1233465456",
"VIN":"V5544257",
"EngineNumber":"E4544564",
"Make":"Porche",
"Model":"911 Turbo",
"Driven":true,
"GVM":"2750kg",
"TARE":"3250kg",
"VehicleClass":"Class 1",
"TitleHolder":"Mr T",
"Owner":"T",
"DateOfFirstRegister":"2018-01-31T22:00:00Z",
"LicenceExpireDate":"2018-02-21T22:00:00Z",
"IsComplete":true,
"IsActive":true,
"InsuranceCompany":"INS001",
"PolicyNumber":"POL001",
"InsuredValue":"R2500000.00",
"FinanceHouse":"None",
"FinanceContactNumber":"None",
"FinanceStartDate":"2018-01-31T22:00:00Z",
"FinanceEndDate":"2018-02-21T22:00:00Z",
"MaintenanceSupplier":"None",
"MaintenanceContactNumber":"123456",
"MaintenanceStartDate":"2018-01-31T22:00:00Z",
"MaintenanceEndDate":"2018-02-27T22:00:00Z",
"Permits":[
{
"Permits":"Harbour Permit",
"PermitTitle":"HB001",
"PermitIssueDate":"2018-01-31T22:00:00Z",
"PermitEndDate":"2018-02-16T22:00:00Z"
},
{
"AssetID":null,
"Permits":"Cross-Border Permit",
"PermitTitle":"C001",
"PermitIssueDate":"2018-02-08T22:00:00Z",
"PermitEndDate":"2018-02-22T22:00:00Z"
}
],
"VehicleTypeSpecificRegistration":"",
"VehicleTypeSpecificRegistrationIssueDate":"",
"VehicleTypeSpecificRegistrationEndDate":"",
"PermitName":null,
"PermitTitle":null
},
"Type":0,
"Created":"2018-02-22T08:39:58.84",
"CreatedById":""
},
]
Any help would be much appreciated!
回答1:
Thanks to Pierre Mallet for the invested time in that detailed answer, that saved me tons of time. But as you can see inside the comments, i had problems to get the this.dataSource.sort and this.dataSource.paginator running.
After i posted my comment, i searched a bit and found out, that there is a problem with the ViewChild from Angular. The ViewChild doesn't catch anything with *ngIf in ngAfterViewInit(). So to get both running, you need to do the following:
Remove the following from ngAfterViewInit()
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
and add the following piece of code:
private paginator: MatPaginator;
private sort: MatSort;
@ViewChild(MatSort) set matSort(ms: MatSort) {
this.sort = ms;
this.setDataSourceAttributes();
}
@ViewChild(MatPaginator) set matPaginator(mp: MatPaginator) {
this.paginator = mp;
this.setDataSourceAttributes();
}
setDataSourceAttributes() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
if (this.paginator && this.sort) {
this.applyFilter('');
}
}
after that, everything should work like a charme. Source of my answer is this -> https://github.com/angular/material2/issues/10205
many thanks to everyone who helped solving this issue!
回答2:
As you data is asynchronous, you try to add the paginator before instantiating the dataSource.
So in order, you have to :
1/ instantiate the data source 2/ after view init "bind" the matSort and matPaginator 3/ when data assets are available, add them as the data of your source
Here is a stackblitz emulating the asynchronous getAssetList with an Observable.of().delay(XXX)
the main topic is :
// 1/ create your instance of matTable
dataSource = new MatTableDataSource<Element>();
.......
// 2/ call the xhr for getting asynchronous data and add it to matTable when available
.......
constructor (.....) {
assetDataService.getAssetList().subscribe(results => {
if (!results) { return };
this.dataSource.data = results;
});
}
.......
// bind sorting, pagination and filtering
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
applyFilter(filterValue: string) {
filterValue = filterValue.trim();
filterValue = filterValue.toLowerCase();
this.dataSource.filter = filterValue;
}
EDITED after data model example :
Your problem is that you cant make default sort/filtering works with nested data structure. The structure to be displayed should be flat and MatColumnDef should match the properties names to make filtering and sorting work out of the box.
So you have to flatten your data before using it in the DataTable
....
displayedColumns = ['fleetNumber', 'make'];
....
// on data resolve
this.dataSource.data = data.map(item => ({
fleetNumber: item.Data.FleetNumber,
make: item.Data.Make
}));
then change the display in the template ( new data structure + lowerCamelCase matColumnDefs ) :
<ng-container matColumnDef="fleetNumber">
<mat-header-cell *matHeaderCellDef mat-sort-header> Fleet Number </mat-header-cell>
<mat-cell *matCellDef="let asset"> {{asset.fleetNumber}} </mat-cell>
</ng-container>
<!-- Make Column -->
<ng-container matColumnDef="make">
<mat-header-cell *matHeaderCellDef mat-sort-header> Make </mat-header-cell>
<mat-cell *matCellDef="let asset"> {{asset.make}} </mat-cell>
</ng-container>
Here is the modified stackblitz
回答3:
I had this issue as well, and the only thing that seemed to work for me, even after making sure I was using [hidden] instead of *ngIf was to use the following lines of code and set the data source after everything had loaded:
dataSource = new MatTableDataSource<T>(this.searchResults);
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatTable) table: MatTable<T>;
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.table.dataSource = this.dataSource;
}
Note that T indicates the Type of the data used in your table, so your MatTableDataSource and MatTable should have the same type. I hope this helps somebody else.
来源:https://stackoverflow.com/questions/48943501/angular-mat-table-datasource-paginator-and-datasource-sort-and-datasource-filter