Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class extends MatTableDataSource and table with filtering

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>
like image 240
KaljaTolkki Avatar asked Oct 05 '18 09:10

KaljaTolkki


People also ask

What is the use of MatColumnDef?

MatColumnDef extends CdkColumnDefDefines a set of cells available for a table column.

What is matCellDef?

A <mat-cell> element which contains the content of the cell. The directive *matCellDef needs to be applied here and the string let user is assigned. By assigning that string we're making sure that we're able to access the current item from the data source via user.


1 Answers

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!

like image 87
chronolegend Avatar answered Sep 16 '22 16:09

chronolegend