Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular mat-table dataSource.paginator and dataSource.sort and dataSource.filter not working as expected

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!

like image 917
onmyway Avatar asked Dec 06 '25 04:12

onmyway


2 Answers

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!

like image 169
Sithys Avatar answered Dec 07 '25 18:12

Sithys


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

like image 26
Pierre Mallet Avatar answered Dec 07 '25 19:12

Pierre Mallet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!