Fix categories loading in side menu component.

This commit is contained in:
Florian THIERRY
2024-08-29 11:09:39 +02:00
parent 5e4068b141
commit d9b856bd43
6 changed files with 75 additions and 37 deletions

View File

@@ -18,6 +18,6 @@ public interface CategoryRepository extends JpaRepository<CategoryEntity, UUID>
""", nativeQuery = true) """, nativeQuery = true)
boolean existsAnyAssociatedPublication(@Param("categoryId") UUID categoryId); boolean existsAnyAssociatedPublication(@Param("categoryId") UUID categoryId);
@Query("SELECT c FROM CategoryEntity c JOIN FETCH c.subCategories") @Query("SELECT c FROM CategoryEntity c LEFT JOIN FETCH c.subCategories")
List<CategoryEntity> findAll(); List<CategoryEntity> findAll();
} }

View File

@@ -1,11 +1,15 @@
<div class="category {{category.isOpenned ? 'openned' : ''}}" *ngFor="let category of categories$ | async"> @for(category of categories$ | async; track category) {
<div class="category {{category.isOpenned ? 'openned' : ''}}">
<div id="category-{{category.id}}" class="category-header" (click)="setOpenned(category)"> <div id="category-{{category.id}}" class="category-header" (click)="setOpenned(category)">
{{category.name}} {{category.name}}
<mat-icon>chevron_right</mat-icon> <mat-icon>chevron_right</mat-icon>
</div> </div>
<div class="sub-category-container {{category.isOpenned ? 'displayed' : ''}}"> <div class="sub-category-container {{category.isOpenned ? 'displayed' : ''}}">
<a [routerLink]="['/']" class="sub-category" *ngFor="let subCategory of category.subCategories"> @for(subCategory of category.subCategories; track subCategory) {
<a [routerLink]="['/categories/' + subCategory.id]" class="sub-category">
{{subCategory.name}} {{subCategory.name}}
</a> </a>
}
</div> </div>
</div> </div>
}

View File

@@ -1,5 +1,5 @@
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { Component, inject } from "@angular/core"; import { Component, inject, OnInit } from "@angular/core";
import { MatIconModule } from "@angular/material/icon"; import { MatIconModule } from "@angular/material/icon";
import { DisplayableCategory, SideMenuService } from "../side-menu.service"; import { DisplayableCategory, SideMenuService } from "../side-menu.service";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
@@ -12,9 +12,13 @@ import { RouterModule } from "@angular/router";
templateUrl: './categories-menu.component.html', templateUrl: './categories-menu.component.html',
styleUrl: './categories-menu.component.scss' styleUrl: './categories-menu.component.scss'
}) })
export class CategoriesMenuComponent { export class CategoriesMenuComponent implements OnInit {
private sideMenuService = inject(SideMenuService); private sideMenuService = inject(SideMenuService);
ngOnInit(): void {
this.sideMenuService.loadCategories();
}
get categories$(): Observable<DisplayableCategory[]> { get categories$(): Observable<DisplayableCategory[]> {
return this.sideMenuService.categories$; return this.sideMenuService.categories$;
} }

View File

@@ -1,8 +1,8 @@
import {Component} from '@angular/core'; import { Component } from '@angular/core';
import {MatIconModule} from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import {CategoriesMenuComponent} from './categories-menu/categories-menu.component'; import { MatTooltipModule } from '@angular/material/tooltip';
import {MatTooltipModule} from '@angular/material/tooltip';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { CategoriesMenuComponent } from './categories-menu/categories-menu.component';
@Component({ @Component({
selector: 'app-side-menu', selector: 'app-side-menu',

View File

@@ -2,6 +2,8 @@ import { Injectable, OnDestroy, inject } from '@angular/core';
import { CategoryService } from '../../core/service/category.service'; import { CategoryService } from '../../core/service/category.service';
import { BehaviorSubject, Observable, Subscription, map } from 'rxjs'; import { BehaviorSubject, Observable, Subscription, map } from 'rxjs';
import { Category } from '../../core/rest-services/category/model/category'; import { Category } from '../../core/rest-services/category/model/category';
import { CategoryRestService } from '../../core/rest-services/category/category.rest-service';
import { MatSnackBar } from '@angular/material/snack-bar';
export interface DisplayableCategory { export interface DisplayableCategory {
id: string; id: string;
@@ -19,26 +21,28 @@ export interface DisplayableSubCategory {
providedIn: 'root' providedIn: 'root'
}) })
export class SideMenuService implements OnDestroy { export class SideMenuService implements OnDestroy {
private categoryService = inject(CategoryService); private categoryRestService = inject(CategoryRestService);
private snackBar = inject(MatSnackBar);
private categoriesSubject = new BehaviorSubject<DisplayableCategory[]>([]); private categoriesSubject = new BehaviorSubject<DisplayableCategory[]>([]);
private categoriesSubscription: Subscription | undefined; private isLoadingSubject = new BehaviorSubject<boolean>(false);
private isLoadedSubject = new BehaviorSubject<boolean>(false);
constructor() { constructor() {
this.categoriesSubscription = this.categoryService.categories$ // this.categoriesSubscription = this.categoryService.categories$
.pipe( // .pipe(
map(categories => // map(categories =>
categories // categories
.filter(category => category.subCategories?.length) // .filter(category => category.subCategories?.length)
.map(category => // .map(category =>
this.mapToDisplayableCategory(category) // this.mapToDisplayableCategory(category)
) // )
) // )
) // )
.subscribe(categories => this.categoriesSubject.next(categories)); // .subscribe(categories => this.categoriesSubject.next(categories));
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.categoriesSubscription?.unsubscribe(); // this.categoriesSubscription?.unsubscribe();
} }
private mapToDisplayableCategory(category: Category): DisplayableCategory { private mapToDisplayableCategory(category: Category): DisplayableCategory {
@@ -57,10 +61,6 @@ export class SideMenuService implements OnDestroy {
} }
} }
get categories$(): Observable<DisplayableCategory[]> {
return this.categoriesSubject.asObservable();
}
private get categories(): DisplayableCategory[] { private get categories(): DisplayableCategory[] {
return this.categoriesSubject.value; return this.categoriesSubject.value;
} }
@@ -69,6 +69,36 @@ export class SideMenuService implements OnDestroy {
this.categoriesSubject.next(categories); this.categoriesSubject.next(categories);
} }
get categories$(): Observable<DisplayableCategory[]> {
return this.categoriesSubject.asObservable();
}
get isLoading$(): Observable<boolean> {
return this.isLoadingSubject.asObservable();
}
loadCategories(): void {
this.isLoadingSubject.next(true);
this.isLoadedSubject.next(false);
this.categoryRestService.getCategories()
.then(categories => {
const displayableCategories = categories
.filter(category => category.subCategories?.length)
.map(category => this.mapToDisplayableCategory(category));
this.categoriesSubject.next(displayableCategories);
})
.catch(error => {
const errorMessage = "An error occured while loading categories.";
console.error(errorMessage, error);
this.snackBar.open(errorMessage, 'Close', { duration: 5000 });
})
.finally(() => {
this.isLoadingSubject.next(false);
this.isLoadedSubject.next(true);
});
}
setOpenned(category: DisplayableCategory): void { setOpenned(category: DisplayableCategory): void {
const categories = this.categories; const categories = this.categories;
const matchingCategory = categories.find(categoryTemp => categoryTemp.id === category.id); const matchingCategory = categories.find(categoryTemp => categoryTemp.id === category.id);