MME-36 Add paginator to recipes-page.
This commit is contained in:
parent
547a23e50a
commit
b97803d31d
@ -5,4 +5,11 @@
|
||||
<p>There was an error loading recipes: {{ loadRecipesError() }}</p>
|
||||
} @else {
|
||||
<app-recipe-card-grid [recipes]="recipes()"></app-recipe-card-grid>
|
||||
<mat-paginator
|
||||
[length]="recipeCount()"
|
||||
[pageIndex]="currentPage()"
|
||||
[pageSize]="pageSize()"
|
||||
[pageSizeOptions]="[5, 10, 25, 50]"
|
||||
(page)="onPage($event)"
|
||||
></mat-paginator>
|
||||
}
|
||||
|
||||
@ -3,10 +3,12 @@ import { RecipeService } from '../../shared/services/RecipeService';
|
||||
import { RecipeCardGrid } from '../../shared/components/recipe-card-grid/recipe-card-grid';
|
||||
import { RecipeInfoView } from '../../shared/models/Recipe.model';
|
||||
import { Spinner } from '../../shared/components/spinner/spinner';
|
||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||
import { combineLatest } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-recipes-page',
|
||||
imports: [RecipeCardGrid, Spinner],
|
||||
imports: [RecipeCardGrid, Spinner, MatPaginator],
|
||||
templateUrl: './recipes-page.html',
|
||||
styleUrl: './recipes-page.css',
|
||||
})
|
||||
@ -17,12 +19,27 @@ export class RecipesPage implements OnInit {
|
||||
protected readonly loadRecipesError = signal<Error | null>(null);
|
||||
protected readonly recipes = signal<RecipeInfoView[]>([]);
|
||||
|
||||
protected readonly recipeCount = signal(50);
|
||||
protected readonly currentPage = signal(0);
|
||||
protected readonly pageSize = signal(10);
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.loadRecipes();
|
||||
}
|
||||
|
||||
private loadRecipes() {
|
||||
this.loadingRecipes.set(true);
|
||||
this.recipeService.getRecipes().subscribe({
|
||||
next: (sliceView) => {
|
||||
combineLatest([
|
||||
this.recipeService.getRecipes({
|
||||
page: this.currentPage(),
|
||||
size: this.pageSize(),
|
||||
}),
|
||||
this.recipeService.getRecipeCount(),
|
||||
]).subscribe({
|
||||
next: ([sliceView, count]) => {
|
||||
this.loadingRecipes.set(false);
|
||||
this.recipes.set(sliceView.content);
|
||||
this.recipeCount.set(count);
|
||||
},
|
||||
error: (e) => {
|
||||
this.loadingRecipes.set(false);
|
||||
@ -30,4 +47,24 @@ export class RecipesPage implements OnInit {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
protected onPage(pageEvent: PageEvent): void {
|
||||
// chart
|
||||
// | size-change | size-same
|
||||
// page-change | reload | reload
|
||||
// page-same | reload | nothing
|
||||
if (pageEvent.pageSize !== this.pageSize() || pageEvent.pageIndex !== this.currentPage()) {
|
||||
this.pageSize.set(pageEvent.pageSize);
|
||||
this.currentPage.update((old) => {
|
||||
if (pageEvent.pageIndex < old) {
|
||||
return Math.max(old - 1, 0);
|
||||
} else if (pageEvent.pageIndex > old) {
|
||||
return (this.currentPage() + 1) * this.pageSize() <= this.recipeCount() ? old + 1 : old;
|
||||
} else {
|
||||
return old;
|
||||
}
|
||||
});
|
||||
this.loadRecipes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { ImageSelect } from '../../../pages/recipe-upload-page/steps/enter-recipe-data/image-select/image-select';
|
||||
import { ImageSelect } from './image-select/image-select';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import {
|
||||
MatCell,
|
||||
@ -34,11 +34,11 @@ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angula
|
||||
import { faBars, faEllipsis } from '@fortawesome/free-solid-svg-icons';
|
||||
import { IngredientDraftClientModel } from '../../client-models/IngredientDraftClientModel';
|
||||
import { ImageView } from '../../models/ImageView.model';
|
||||
import { IngredientDialog } from '../../../pages/recipe-upload-page/steps/enter-recipe-data/ingredient-dialog/ingredient-dialog';
|
||||
import { IngredientDialog } from './ingredient-dialog/ingredient-dialog';
|
||||
import { RecipeDraftViewModel } from '../../models/RecipeDraftView.model';
|
||||
import { RecipeEditFormSubmitEvent } from './RecipeEditFormSubmitEvent';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ImageUploadDialog } from '../../../pages/recipe-upload-page/steps/enter-recipe-data/image-upload-dialog/image-upload-dialog';
|
||||
import { ImageUploadDialog } from './image-upload-dialog/image-upload-dialog';
|
||||
import { FullRecipeView } from '../../models/Recipe.model';
|
||||
|
||||
@Component({
|
||||
|
||||
@ -16,6 +16,17 @@ import { RecipeUpdateBody } from '../bodies/RecipeUpdateBody';
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RecipeService {
|
||||
public static readonly RecipeProperties = [
|
||||
'id',
|
||||
'created',
|
||||
'modified',
|
||||
'title',
|
||||
'slug',
|
||||
'preparationTime',
|
||||
'cookingTime',
|
||||
'totalTime',
|
||||
] as const;
|
||||
|
||||
private readonly http = inject(HttpClient);
|
||||
private readonly authService = inject(AuthService);
|
||||
private readonly queryClient = inject(QueryClient);
|
||||
@ -46,8 +57,12 @@ export class RecipeService {
|
||||
};
|
||||
}
|
||||
|
||||
public getRecipes(): Observable<SliceView<RecipeInfoView>> {
|
||||
return this.http.get<SliceView<WithStringDates<RecipeInfoView>>>(this.endpointService.getUrl('recipes')).pipe(
|
||||
public getRecipes(
|
||||
queryParams?: QueryParams<typeof RecipeService.RecipeProperties>,
|
||||
): Observable<SliceView<RecipeInfoView>> {
|
||||
return this.http
|
||||
.get<SliceView<WithStringDates<RecipeInfoView>>>(this.endpointService.getUrl('recipes', [], queryParams))
|
||||
.pipe(
|
||||
map((sliceView) => ({
|
||||
...sliceView,
|
||||
content: sliceView.content.map((withStringDates) => this.hydrateRecipeInfoView(withStringDates)),
|
||||
@ -55,6 +70,12 @@ export class RecipeService {
|
||||
);
|
||||
}
|
||||
|
||||
public getRecipeCount(): Observable<number> {
|
||||
return this.http
|
||||
.get<{ count: number }>(this.endpointService.getUrl('recipes', ['meta', 'count']))
|
||||
.pipe(map((res) => res.count));
|
||||
}
|
||||
|
||||
public getRecipeView(username: string, slug: string): Promise<FullRecipeViewWrapper> {
|
||||
return firstValueFrom(
|
||||
this.http.get<FullRecipeViewWrapper>(this.endpointService.getUrl('recipes', [username, slug])),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user