Issue
I’m trying to implement Spring pagination with Angular. I tried this code:
Search DTO:
public class ClassCategoriesSearchParams {
private String title;
private String type;
private LocalDateTime publishedAt;
}
Endpoint:
@PostMapping("/find")
public Page<ClassCategoriesFullDTO> search(@Valid ClassCategoriesSearchParams params, Pageable pageable) {
Page<ClassCategoriesFullDTO> list = classCategoriesRestService.findClasses(params, pageable);
return list;
}
public Page<ClassCategoriesFullDTO> findClasses(ClassCategoriesSearchParams params, Pageable pageable) {
Specification<Product> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (params.getTitle() != null) {
predicates.add(cb.equal(root.get("title"), params.getTitle()));
}
if (params.getType() != null) {
predicates.add(cb.equal(root.get("type"), params.getType()));
}
if (params.getPublishedAt() != null) {
predicates.add(cb.greaterThan(root.get("publishedAt"), params.getPublishedAt()));
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
return classCategoriesService.findAll(spec, pageable).map(classCategoriesMapper::toFullDTO);
}
Angular code:
<div class="d-flex justify-content-between p-2">
<ngb-pagination [collectionSize]="totalItems" [(page)]="page" [pageSize]="size" (pageChange)="pageChange($event)">
</ngb-pagination>
<select class="custom-select" style="width: auto" [(ngModel)]="size" (ngModelChange)="sizeChange($event)">
<option [ngValue]="1">1 items per page</option>
<option [ngValue]="2">2 items per page</option>
<option [ngValue]="4">4 items per page</option>
<option [ngValue]="6">6 items per page</option>
</select>
</div>
Typescript code:
export class ClassListComponent implements OnInit {
public page = 0;
public size = 1;
public totalItems = 0;
public productList = [];
public classes = [
{
id: null,
productImage: null,
summary: '',
title: '',
imgSrc: null
}
];
public loading = false;
constructor(
private _classService: ClassService,
private sanitizer: DomSanitizer
) { }
ngOnInit(): void {
this.getClassList();
this.getAllProducts();
}
private getClassList(): void {
this._classService.getCategories().subscribe(
data => {
this.classes = [];
this.classes = Object.values(data);
this.classes.forEach(element => {
this.getCategoryImage(element);
});
},
);
}
public getCategoryImage(element): void {
this.loading = true;
this._classService.getCategoryImage(element.id).subscribe(
data => {
element.imgSrc = this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(data));
this.loading = false;
}
);
}
private getAllProducts(): void {
this._classService.getAllProducts(this.page, this.size).subscribe(
response => {
this.productList = [];
this.productList = response.content;
this.totalItems = response.totalElements;
}
);
}
public pageChange(event): void {
this.page = event - 1;
this.getAllProducts();
}
public sizeChange(event): void {
this.size = event;
this.getAllProducts();
}
}
Service:
public getAllProducts(
page?: number,
size?: number
): Observable<GenericListDTO<any>> {
let params = new HttpParams();
if (page !== null) {
params = params.set('page', page.toString());
}
if (size) {
params = params.set('size', size.toString());
}
return this.http.post<GenericListDTO<any>>(`${this.url}/find`, {}, { params });
}
export class GenericListDTO<T> {
content: T[] = [];
pageable: any = {};
totalPages: number;
totalElements: number;
size: number;
numberOfElements: number;
}
I have this issue: When I set the number of items per page to be 1, in order to get the first item I need to send param page = 0. I can set the logic of page change function to behave like expected but my pagination component will not correctly focus the selected page. Maybe this could be fixed with some css overriding, but I think the cleaner solution would be to change this in BE function. Do you know how this can be fixed?
Solution
You can have a new state variable for your service with one subtracted instead of modifying page
variable.
Alternately, Spring has support for indexing pagination starting from 1. Two way you can configure
spring.data.web.pageable.one-indexed-parameters=true
in your application properties- Through java config, modify the resolver bean
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setOneIndexedParameters(true);