Reputation: 163
I am trying my hands on Angular for a project and I am stuck with creating a data source for my MatTable.
The API that I am consuming sends response like this:
{
data: [], //array of objects for samples
total: 100, //total number of samples found
pageSize: 10,
pageIndex: 0
}
My model for samples is:
//Model for a single sample
export class Sample {
id: number;
name: string;
}
//a plural namespace is used for multiple samples returned from API
export class Samples {
samples: Sample[],
total: number;
pageSize: number;
pageIndex: number;
}
// a const in case no samples were found
export const NO_SAMPLES {
total: 0,
pageIndex: 0,
pageSize: 0,
samples: []
}
Now when I am trying to integrated this with a data source like below:
... //required imports
export class SamplesDataSource extends DataSource<Samples> {
private samplesSubject = new BehaviorSubject<Samples>(NO_SAMPLES);
public constructor(private samplesService: SamplesService) {
super();
}
//this is where the error (described below) is showing
connect(collectionViewer: CollectionViewer): Observable<Samples> {
return this.samplesSubject.asObservable();
}
disconnect(collectionViewer: CollectionViewer): void {
this.samplesSubject.complete();
}
getSamples() {
this.samplesService.getSamples().pipe(
catchError(err => {
return of([]);
})
).subscribe(
samples => this.samplesSubject.next(samples)
);
}
}
But it shows me error message:
ERROR in src/app/shared/data-sources/samples-data-source.ts(20,5): error TS2416: Property 'connect' in type 'SamplesDataSource' is not assignable to the same property in base type 'DataSource'
How can I handle this case.
Please note, I need to retain total
, pageSize
and pageIndex
for my paginator to work in align with the paginator at backend.
Thanks in advance.
App component.ts
... //required imports
export class AlertCasesComponent implements OnInit {
...
samplesTableColumns: string[] = ['sampleId', 'sample'];
samplesTablePageSizes: number[] = [10, 20, 50, 100];
samplesDataSource: SamplesDataSource;
...
constructor(samplesService: SamplesService) {
...
}
ngOnInit(): void {
this.samplesDataSource = new SamplesDataSource(this.samplesService);
this.samplesDataSource.getSamples();
...
}
...
}
App Component.html
<mat-table class="..." [dataSource]="samplesDataSource.samples">
.... //rows and columns implementations
</mat-table>
<mat-paginator [length]="samplesDataSource.total" pageIndex="0" [pageSize]="samplesDataSource.pageSize" [pageSizeOptions]="samplesTablePageSizes" showFirstLastButtons></mat-paginator>
Upvotes: 1
Views: 3060
Reputation: 40602
It's because the DataSource.connect()
should return an observable that emits an array of data. So, the SamplesDataSource should return array of samples, like:
export class SamplesDataSource extends DataSource<Sample> {
private samplesSubject = new BehaviorSubject<Samples>(NO_SAMPLES);
connect(collectionViewer: CollectionViewer): Observable<Sample[]> {
return this.samplesSubject.asObservable().pipe(
map(samples => samples.samples)
);
}
...
}
Here is the official CDK table example, I hope it will help you. stackblitz version is also available.
It isn't necessary to use DataSource
class in your case, you can use an array of samples as the data source. I've created the stackblitz example.
table-basic-example.ts
import { Component, Injectable } from '@angular/core';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { switchMap, delay } from 'rxjs/operators';
import { PageEvent } from '@angular/material/paginator';
export interface Sample {
id: number;
name: string;
}
export interface Samples {
samples: Sample[];
total: number;
pageSize: number;
pageIndex: number;
}
const ELEMENT_DATA: Sample[] = [
{id: 1, name: 'Hydrogen'},
{id: 2, name: 'Helium'},
{id: 3, name: 'Lithium'},
{id: 4, name: 'Beryllium'},
{id: 5, name: 'Boron'},
{id: 6, name: 'Carbon'},
{id: 7, name: 'Nitrogen'},
{id: 8, name: 'Oxygen'},
{id: 9, name: 'Fluorine'},
{id: 10, name: 'Neon'}
];
@Injectable()
export class SamplesService {
getSamples(pageIndex: number, pageSize: number): Observable<Samples> {
const start = pageIndex * pageSize;
const samples = ELEMENT_DATA.slice(start, start + pageSize);
return of<Samples>({
samples: samples,
pageIndex: pageIndex,
pageSize: pageSize,
total: ELEMENT_DATA.length
}).pipe(
delay(500)
);
}
}
@Component({
selector: 'table-basic-example',
templateUrl: 'table-basic-example.html',
styleUrls: ['table-basic-example.css'],
providers: [ SamplesService ]
})
export class TableBasicExample {
readonly displayedColumns: string[] = ['id', 'name'];
readonly page$ = new BehaviorSubject({
index: 0,
size: 4
});
samples: Samples = {
total: 0,
pageIndex: 0,
pageSize: 0,
samples: []
};
constructor(
private readonly samplesService: SamplesService
) {
this.page$.pipe(
switchMap(page => {
return this.samplesService.getSamples(page.index, page.size);
})
).subscribe(samples => {
this.samples = samples;
});
}
onPageChanged(event: PageEvent) {
this.page$.next({
index: event.pageIndex,
size: event.pageSize
});
}
}
table-basic-example.html
<table mat-table [dataSource]="samples.samples" class="mat-elevation-z8">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef> No. </th>
<td mat-cell *matCellDef="let element"> {{element.id}} </td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator
[length]="samples.total"
pageIndex="pageIndex$.value"
[pageSize]="samples.pageSize"
[pageSizeOptions]="[4, 9]"
showFirstLastButtons
(page)="onPageChanged($event)"></mat-paginator>
Upvotes: 3