I have a CRUD REST API which sends paginated data to the client. Consider the following data interface:
export interface LCKData {
// Some basic type fields from DB
}
export interface LCKPageData {
content: LCKData[],
pageable: any,
totalElements: number,
totalPages: number,
last: boolean,
numberOfElements: number,
size: number,
number: number,
sort: any,
first: boolean,
empty: boolean
}
The second interface is the standard paginated data from Spring Boot web application. I use this structure to set the Paginator component properties, i.e. number of total pages.
I use these two services to fetch data and create a DataSource:
let LCKBaseAddress: string = "...";
@Injectable()
export class LockService {
constructor(private http: HttpClient) { }
findLCK(sortColumn = "sysTime", sortOrder = 'desc',
pageNumber = 0, pageSize = 10): Observable<LCKPageData> {
return this.http.get(LCKBaseAddress, {
params: new HttpParams()
.set('q', sortColumn)
.set('d', sortOrder)
.set('p', pageNumber.toString())
.set('s', pageSize.toString())
}).pipe(map(res => {
res['payload'] = res;
return res["payload"];
}))
};
}
And:
export class MainService extends DataSource<LCKData>{
private loadingSubject = new BehaviorSubject<boolean>(false);
private pageSubject = new BehaviorSubject<LCKData[]>([]);
private locks$ = new Observable<LCKPageData>();
constructor(private lockService: LockService) {
super();
};
connect(collectionViewer: CollectionViewer): Observable<LCKData[]> {
return this.locks$.pipe(pluck("content"));
}
disconnect(collectionViewer: CollectionViewer): void {
this.loadingSubject.complete();
this.pageSubject.complete();
}
loadLCK(sortColumn = "sys_time", sortOrder = 'desc',
pageNumber = 0, pageSize = 10) {
this.lockService.findLCK(sortColumn, sortOrder, pageNumber, pageSize).pipe(
catchError(() => of([])),
finalize(() => this.loadingSubject.next(false))
)
.subscribe(lcks => this.pageSubject.next(lcks['content']));
}
}
And finally the table component:
@Component({
selector: 'app-table',
templateUrl: './table.component.html',
styleUrls: ['./table.component.css']
})
export class TableComponent implements OnInit {
public columns: string[];
dataSource: MainService;
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
constructor(public lock: LockService) {}
ngOnInit(): void {
this.dataSource = new MainService(this.lock);
this.dataSource.loadLCK();
this.columns = [ // Some columns //];
}
ngAfterViewInit() {
this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);
merge(this.sort.sortChange, this.paginator.page)
.pipe(
tap(() => this.loadLessonsPage())
)
.subscribe();
}
loadLessonsPage() {
this.dataSource.loadLCK(
'sysTime',
this.sort.direction,
this.paginator.pageIndex,
this.paginator.pageSize);
}
}
And the Template:
<table mat-table [dataSource]="dataSource" class="mat-elevation-z2" matSort matSortActive="sysTime"
matSortDirection="desc" matSortDisableClear>
// Column and row definition
The code runs without any errors but nothing is displayed in the table.
Previously I have tested a same approach with non-paginated data and everything was OK, where the server responded List<LCKData> instead of LCKPageData.
I use Angular 10.
Any ideas?
Your connect()
method returns an observable that never emits!
Instead of initializing locks$
to be a new observable, you should define it off of your pageSubject
.
private locks$: Observable<LCKData[]> = this.pageSubject.asObservable();
No need to use pluck
since you're already emitting just the content
property into the subject (this.pageSubject.next(lcks['content'])
):
connect(collectionViewer: CollectionViewer): Observable<LCKData[]> {
return this.locks$;
}