Class extends MatTableDataSource and table with filtering
Asked Answered
T

1

6

This is my hobby project and it has been stuck due of this issue for some time. It might be an easy issue, but my knowledge about Angular and JS is rather limited.. Nevertheless my code is below (I have shorten it a bit) and it is working to some extent. It is fetching data from server and then it is displayed at client. No issues there, but now when I am trying to do client side filtering then nothing happens. Literally. I am typing into filter input box and nothing. Table rows are not filtered.

I am wondering here two things:

  1. Do I use right approach (can I extend MatTableDataSource)?
  2. What I am doing wrong (if I can extend MatTableDataSource)?

MyData.ts

export interface MyData {
    id: number;
    description: string;
}

MyData.service.ts

export class MyService {

    constructor(private http: HttpClient) { }

    getData(): Observable<MyData[]> {
        return this.http.get...
    }
}

MyData.datasource.ts

export class MyDataSource extends MatTableDataSource<MyData> {

    private mySubject = new BehaviorSubject<MyData[]>([]);

    constructor(private myService: MyService) { super(); }

    loadData() {
        this.myService.getData()
        .pipe(catchError(() => of([])))
        .subscribe(data => this.mySubject.next(data));
    }

    connect(): BehaviorSubject<myData[]> {
        return this.mySubject;
    }

    disconnect(): void {
        this.mySubject.complete();
    }
}

MyData.component.ts

export class MyDataComponent implements OnInit {

    displayedColumns= ["id", "description"];
    dataSource: MyDataSource;

    constructor(private myService: MyService) { }

    ngOnInit() {
        this.dataSource = new MyDataSource(this.myService);
        this.dataSource.loadData();
    }

        applyFilter(filterValue: string) {
            this.dataSource.filter = filterValue.trim().toLowerCase();
        }
}

MyData.component.html

<mat-form-field>
    <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>

<mat-table [dataSource]="dataSource">

    <ng-container matColumnDef="id">
        <mat-header-cell *matHeaderCellDef>ID</mat-header-cell>
        <mat-cell *matCellDef="let data">{{data.id}}</mat-cell>
    </ng-container>

    <ng-container matColumnDef="description">
        <mat-header-cell *matHeaderCellDef>Description</mat-header-cell>
        <mat-cell *matCellDef="let data">{{data.description}}</mat-cell>
    </ng-container>

</mat-table>
Tonneau answered 5/10, 2018 at 9:12 Comment(5)
blog.angular-university.io/angular-material-data-tableFissile
I have read that and it is using server side filtering.Tonneau
If you use client filtering than simply use MatTableDataSource. If you override connect method then you have to write your own code to handle filtering, sorting and paging. Assigning value to this.dataSource.filter you're firing filtering on inner sourceFissile
So the best approach would be to get rid of MyDataSource and calling MyService directly from MyComponent which would return an array of data that I would use to instantiate MatTableDataSource?Tonneau
Yep something like this. But if you want to practice then you can take a look at the source code github.com/angular/material2/blob/… but for client implementation you just reinvent the wheel.Fissile
L
1

Yes you can extend data source if you want to have more control over your data for example custom sorting, filtering, pagination and real time streaming/manipulation of data. If not you can just use the default data source class provided in the material website

https://material.angular.io/components/table/overview

As stated in the material site above you can extend the datasource class if you want to do more complex stuff

Advanced data sources
The simplest way to provide data to your table is by passing a data array. More complex use-cases may benefit from a more flexible approach involving an Observable stream or by encapsulating your data source logic into a DataSource class.

Here is a more complex example on how you can use it.

The below code is the constructor of the datasource. Which takes in the paginator, dataservice and Mat sort.

  constructor(public _dataService: DataService,
              public _paginator: MatPaginator,
              public _sort: MatSort) {
    super();
    // Reset to the first page when the user changes the filter.
    this._filterChange.subscribe(() => this._paginator.pageIndex = 0);
  }

Then you will implement the connect class. This connect class is an observable where your mat-table will subcribe to this observable and display data accordingly to what the observable returns

  connect(): Observable<Array<Data>> {
    // Listen for any changes in the base data, sorting, filtering, or 
    //pagination the below object types are all observable/behaviour 
    //subjects
    const displayDataChanges = [
      this._dataService.entryDataChange,
      this._sort.sortChange,
      this._filterChange,
      this._paginator.page
    ]; 
    data: YourCurrentData;
    // Merge all the observable into one stream
    return Observable.merge(...displayDataChanges).map((n) => {
      // If is filter data observer do action
      // If is a sort observer emitting data do action
      // If is new incoming data do action
      // If is a paginator observable emmiting data do action
      // Return filtered, sorted, and paged data that you want to display
      // on your current page. 
    });
  }

The above example uses the old rxjs version. But hope you get the logic behind it!

Longsome answered 26/10, 2018 at 3:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.