Compare commits

7 Commits

Author SHA1 Message Date
Florian THIERRY
a2de24fd93 Convert observables to signals. 2026-02-02 17:51:49 +01:00
Florian THIERRY
ebe46a0d11 Convert observables to signals. 2026-02-02 17:38:57 +01:00
Florian THIERRY
a3637faafa Rework picture selector to use signals. 2026-02-02 17:29:49 +01:00
Florian THIERRY
987d187c44 Rework search page to use signals. 2026-02-02 17:26:30 +01:00
Florian THIERRY
bf7e755c28 Fix page display after receiving data from backend. 2026-02-02 17:14:21 +01:00
Florian THIERRY
241f765648 Upgrade package-lock.json 2026-02-02 16:56:55 +01:00
Florian THIERRY
20782cd45a Upgrade frontend dependencies. 2026-02-02 16:55:32 +01:00
21 changed files with 1173 additions and 1847 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -17,32 +17,26 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^21.0.6", "@angular/animations": "^21.1.2",
"@angular/cdk": "^21.0.5", "@angular/cdk": "^21.1.2",
"@angular/common": "^21.0.6", "@angular/common": "^21.1.2",
"@angular/compiler": "^21.0.6", "@angular/compiler": "^21.1.2",
"@angular/core": "^21.0.6", "@angular/core": "^21.1.2",
"@angular/forms": "^21.0.6", "@angular/forms": "^21.1.2",
"@angular/material": "^21.0.5", "@angular/material": "^21.1.2",
"@angular/platform-browser": "^21.0.6", "@angular/platform-browser": "^21.1.2",
"@angular/platform-browser-dynamic": "^21.0.6", "@angular/platform-browser-dynamic": "^21.1.2",
"@angular/router": "^21.0.6", "@angular/router": "^21",
"rxjs": "~7.8.0", "rxjs": "~7.8.2",
"tslib": "^2.3.0", "tslib": "^2.8.1"
"zone.js": "~0.15.1"
}, },
"devDependencies": { "devDependencies": {
"@angular/build": "^21.0.4", "@angular/build": "^21.1.2",
"@angular/cli": "^21.0.4", "@angular/cli": "^21.1.2",
"@angular/compiler-cli": "^21.0.6", "@angular/compiler-cli": "^21.1.2",
"@angular/localize": "^21.0.6", "@angular/localize": "^21.1.2",
"@types/jasmine": "~5.1.0", "@types/jasmine": "~5.1.15",
"jasmine-core": "~5.1.0", "jasmine-core": "~5.13.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.9.3" "typescript": "~5.9.3"
} }
} }

View File

@@ -1,8 +1,8 @@
<div i18n> <div i18n>
<span class="copy-left">&copy;</span> <span class="copy-left">&copy;</span>
2016 - 2024 All rights reserved 2016 - 2026 All rights reserved
- -
2.1 2.2
<a [routerLink]="['./']" matTooltip="Health checking will be available in future..." i18n-matTooltip> <a [routerLink]="['./']" matTooltip="Health checking will be available in future..." i18n-matTooltip>
<mat-icon>favorite</mat-icon> <mat-icon>favorite</mat-icon>
</a> </a>
@@ -11,4 +11,4 @@
<mat-icon matTooltip="Documentation will be available in future..." i18n-matTooltip>menu_book</mat-icon> <mat-icon matTooltip="Documentation will be available in future..." i18n-matTooltip>menu_book</mat-icon>
- -
<span i18n>Development realised by</span> Florian THIERRY <span i18n>Development realised by</span> Florian THIERRY
</div> </div>

View File

@@ -12,6 +12,7 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
gap: .2em;
.copy-left { .copy-left {
transform: rotate(180deg); transform: rotate(180deg);
@@ -28,4 +29,4 @@
align-items: center; align-items: center;
} }
} }
} }

View File

@@ -9,6 +9,4 @@ import { RouterModule } from '@angular/router';
templateUrl: './footer.component.html', templateUrl: './footer.component.html',
styleUrl: './footer.component.scss' styleUrl: './footer.component.scss'
}) })
export class FooterComponent { export class FooterComponent {}
}

View File

@@ -10,7 +10,7 @@
<h1 i18n>Select an illustration</h1> <h1 i18n>Select an illustration</h1>
</header> </header>
<div class="picture-container"> <div class="picture-container">
@if (isLoading) { @if (isLoading()) {
<h2 i18n>Pictures loading...</h2> <h2 i18n>Pictures loading...</h2>
<mat-spinner></mat-spinner> <mat-spinner></mat-spinner>
} @else { } @else {
@@ -40,4 +40,4 @@
Add new picture Add new picture
</button> </button>
<input type="file" (change)="uploadPicture($event)" #fileUpload/> <input type="file" (change)="uploadPicture($event)" #fileUpload/>
</footer> </footer>

View File

@@ -1,11 +1,11 @@
import { Component, inject, OnInit } from "@angular/core"; import {Component, inject, OnInit, signal} from "@angular/core";
import { Picture } from "../../../core/rest-services/picture/model/picture"; import { Picture } from "../../../core/rest-services/picture/model/picture";
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import { MatSnackBar } from "@angular/material/snack-bar"; import { MatSnackBar } from "@angular/material/snack-bar";
import { PictureRestService } from "../../../core/rest-services/picture/picture.rest-service"; import { PictureRestService } from "../../../core/rest-services/picture/picture.rest-service";
import { MatIcon } from "@angular/material/icon"; import { MatIcon } from "@angular/material/icon";
import { MatDialogRef } from "@angular/material/dialog"; import { MatDialogRef } from "@angular/material/dialog";
import {MatRippleModule} from '@angular/material/core'; import {MatRippleModule} from '@angular/material/core';
import { MatTooltip } from "@angular/material/tooltip"; import { MatTooltip } from "@angular/material/tooltip";
@Component({ @Component({
@@ -24,12 +24,12 @@ export class PictureSelectionDialog implements OnInit {
private readonly snackBar = inject(MatSnackBar); private readonly snackBar = inject(MatSnackBar);
private readonly dialogRef = inject(MatDialogRef<PictureSelectionDialog>); private readonly dialogRef = inject(MatDialogRef<PictureSelectionDialog>);
isLoading: boolean = false; isLoading = signal(false);
isLoaded: boolean = false; isLoaded = signal(false);
pictures: Picture[] = []; pictures: Picture[] = [];
ngOnInit(): void { ngOnInit(): void {
this.isLoading = true; this.isLoading.set(true);
this.pictureRestService.getAllOfCurrentUser() this.pictureRestService.getAllOfCurrentUser()
.then(pictures => { .then(pictures => {
this.pictures = pictures; this.pictures = pictures;
@@ -38,14 +38,14 @@ export class PictureSelectionDialog implements OnInit {
if (error.status === 401) { if (error.status === 401) {
this.dialogRef.close(); this.dialogRef.close();
} else { } else {
const errorMessage = $localize`An error occured while loading pictures.`; const errorMessage = $localize`An error occurred while loading pictures.`;
console.error(errorMessage, error); console.error(errorMessage, error);
this.snackBar.open(errorMessage, $localize`Close`, { duration: 5000 }); this.snackBar.open(errorMessage, $localize`Close`, { duration: 5000 });
} }
}) })
.finally(() => { .finally(() => {
this.isLoading = false; this.isLoading.set(false);
this.isLoaded = true; this.isLoaded.set(true);
}); });
} }
@@ -65,10 +65,10 @@ export class PictureSelectionDialog implements OnInit {
this.dialogRef.close(pictureId); this.dialogRef.close(pictureId);
}) })
.catch(error => { .catch(error => {
const errorMessage = $localize`A technical error occured while uploading your picture.`; const errorMessage = $localize`A technical error occurred while uploading your picture.`;
console.error(errorMessage, error); console.error(errorMessage, error);
this.snackBar.open(errorMessage, $localize`Close`, { duration: 5000 }); this.snackBar.open(errorMessage, $localize`Close`, { duration: 5000 });
}); });
} }
} }
} }

View File

@@ -1,4 +1,4 @@
@for(publication of publications$ | async; track publication) { @for(publication of publications(); track publication.id) {
<a [routerLink]="['/publications/' + publication.id]" class="publication"> <a [routerLink]="['/publications/' + publication.id]" class="publication">
<img src="/api/pictures/{{ publication.illustrationId }}"/> <img src="/api/pictures/{{ publication.illustrationId }}"/>
<div class="body"> <div class="body">
@@ -13,4 +13,4 @@
</span> </span>
</div> </div>
</a> </a>
} }

View File

@@ -1,4 +1,4 @@
import { Component, Input } from "@angular/core"; import {Component, input, Input} from "@angular/core";
import { Publication } from "../../core/rest-services/publications/model/publication"; import { Publication } from "../../core/rest-services/publications/model/publication";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
@@ -12,6 +12,5 @@ import { MatTooltipModule } from "@angular/material/tooltip";
imports: [CommonModule, RouterModule, MatTooltipModule] imports: [CommonModule, RouterModule, MatTooltipModule]
}) })
export class PublicationListComponent { export class PublicationListComponent {
@Input() publications = input.required<Publication[]>();
publications$!: Observable<Publication[]>; }
}

View File

@@ -1,4 +1,4 @@
<div class="menu {{ isOpenned ? 'displayed' : '' }}"> <div class="menu {{ isOpened ? 'displayed' : '' }}">
<h1> <h1>
<a [routerLink]="['/home']"> <a [routerLink]="['/home']">
<img src="assets/images/codiki.png" alt="logo"/> <img src="assets/images/codiki.png" alt="logo"/>
@@ -16,4 +16,4 @@
<h2 i18n>Categories</h2> <h2 i18n>Categories</h2>
<app-categories-menu (categoryClicked)="close()"></app-categories-menu> <app-categories-menu (categoryClicked)="close()"></app-categories-menu>
</div> </div>
<div class="overlay {{ isOpenned ? 'displayed' : ''}}" (click)="close()"></div> <div class="overlay {{ isOpened ? 'displayed' : ''}}" (click)="close()"></div>

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core'; import {Component, signal} from '@angular/core';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@@ -18,13 +18,13 @@ import { MatRippleModule } from '@angular/material/core';
] ]
}) })
export class SideMenuComponent { export class SideMenuComponent {
isOpenned: boolean = false; isOpened = signal(false);
open(): void { open(): void {
this.isOpenned = true; this.isOpened.set(true);
} }
close(): void { close(): void {
this.isOpenned = false; this.isOpened.set(false);
} }
} }

View File

@@ -1,12 +1,12 @@
<button type="submit" <button type="submit"
class="cod-button {{color}}" class="cod-button {{color()}}"
[disabled]="disabled || requestPending" [disabled]="disabled() || requestPending()"
(click)="click.emit()" (click)="click.emit()"
matRipple> matRipple>
@if (requestPending) { @if (requestPending()) {
<mat-spinner class="spinner {{color}}" [diameter]="25"></mat-spinner> <mat-spinner class="spinner {{color()}}" [diameter]="25"></mat-spinner>
} }
<span> <span>
<ng-content/> <ng-content/>
</span> </span>
</button> </button>

View File

@@ -1,5 +1,5 @@
import { Component, EventEmitter, Input, Output } from "@angular/core"; import {Component, EventEmitter, input, Input, output, Output, signal} from "@angular/core";
import { MatRippleModule } from "@angular/material/core"; import { MatRippleModule } from "@angular/material/core";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner"; import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
@@ -13,10 +13,9 @@ import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
] ]
}) })
export class SubmitButtonComponent { export class SubmitButtonComponent {
@Input() requestPending: boolean = false; requestPending = input.required<boolean>();
@Input() label: string = ''; label = input<string>();
@Input() disabled: boolean = false; disabled = input.required<boolean>();
@Input() color?: 'secondary'; color = input<'secondary' | undefined>('secondary');
@Output() click = new EventEmitter<void>(); click = output<void>();
} }

View File

@@ -1,10 +1,10 @@
<h1 i18n>Last publications</h1> <h1 i18n>Last publications</h1>
@if ((isLoading$ | async) === true) { @if ((isLoading())) {
<h2 i18n>Publications loading...</h2> <h2 i18n>Publications loading...</h2>
<mat-spinner></mat-spinner> <mat-spinner />
} @else { } @else {
@if ((publications$ | async) != []) { @if (publications(); as publications) {
<app-publication-list [publications$]="publications$"></app-publication-list> <app-publication-list [publications]="publications" />
} @else { } @else {
<h2 i18n>No any publication.</h2> <h2 i18n>No any publication.</h2>
} }

View File

@@ -1,10 +1,11 @@
import { Component, OnInit, inject } from '@angular/core'; import {Component, OnInit, inject, Signal} from '@angular/core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { PublicationListComponent } from '../../components/publication-list/publication-list.component'; import { PublicationListComponent } from '../../components/publication-list/publication-list.component';
import { Publication } from '../../core/rest-services/publications/model/publication'; import { Publication } from '../../core/rest-services/publications/model/publication';
import { HomeService } from './home.service'; import { HomeService } from './home.service';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import {toSignal} from "@angular/core/rxjs-interop";
@Component({ @Component({
selector: 'app-home', selector: 'app-home',
@@ -20,12 +21,12 @@ import { CommonModule } from '@angular/common';
export class HomeComponent implements OnInit { export class HomeComponent implements OnInit {
private homeService = inject(HomeService); private homeService = inject(HomeService);
get isLoading$(): Observable<boolean> { get isLoading(): Signal<boolean> {
return this.homeService.isLoading$; return toSignal(this.homeService.isLoading$, { initialValue: false });
} }
get publications$(): Observable<Publication[]> { get publications(): Signal<Publication[]> {
return this.homeService.publications$; return toSignal(this.homeService.publications$, { initialValue: [] });
} }
ngOnInit(): void { ngOnInit(): void {

View File

@@ -9,13 +9,13 @@
+ +
</a> </a>
@if ((isLoading$ | async) === true) { @if (isLoading()) {
<h2 i18n>Publication loading...</h2> <h2 i18n>Publication loading...</h2>
<mat-spinner></mat-spinner> <mat-spinner></mat-spinner>
} @else { } @else {
@if ((isLoaded$ | async) === true) { @if (isLoaded()) {
<app-publication-list [publications$]="publications$"></app-publication-list> <app-publication-list [publications]="publications()"></app-publication-list>
} @else { } @else {
<h2 i18n>There is no any publication...</h2> <h2 i18n>There is no any publication...</h2>
} }
} }

View File

@@ -1,4 +1,4 @@
import { Component, inject, OnInit } from "@angular/core"; import {Component, inject, OnInit, Signal} from "@angular/core";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner"; import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MyPublicationsService } from "./my-publications.service"; import { MyPublicationsService } from "./my-publications.service";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
@@ -8,6 +8,7 @@ import { CommonModule } from "@angular/common";
import { RouterModule } from "@angular/router"; import { RouterModule } from "@angular/router";
import { MatTooltipModule } from "@angular/material/tooltip"; import { MatTooltipModule } from "@angular/material/tooltip";
import { MatRippleModule } from "@angular/material/core"; import { MatRippleModule } from "@angular/material/core";
import {toSignal} from "@angular/core/rxjs-interop";
@Component({ @Component({
@@ -27,19 +28,19 @@ import { MatRippleModule } from "@angular/material/core";
export class MyPublicationsComponent implements OnInit { export class MyPublicationsComponent implements OnInit {
private readonly myPublicationsService = inject(MyPublicationsService); private readonly myPublicationsService = inject(MyPublicationsService);
get publications$(): Observable<Publication[]> { get publications(): Signal<Publication[]> {
return this.myPublicationsService.publications$; return toSignal(this.myPublicationsService.publications$, { initialValue: [] });
} }
get isLoading$(): Observable<boolean> { get isLoading(): Signal<boolean> {
return this.myPublicationsService.isLoading$; return toSignal(this.myPublicationsService.isLoading$, { initialValue: false });
} }
get isLoaded$(): Observable<boolean> { get isLoaded(): Signal<boolean> {
return this.myPublicationsService.isLoaded$; return toSignal(this.myPublicationsService.isLoaded$, { initialValue: false });
} }
ngOnInit(): void { ngOnInit(): void {
this.myPublicationsService.loadAuthenticatedUserPublications(); this.myPublicationsService.loadAuthenticatedUserPublications();
} }
} }

View File

@@ -1,14 +1,14 @@
@if (isLoading) { @if (isLoading()) {
<h2 i18n>Publication content loading...</h2> <h2 i18n>Publication content loading...</h2>
<mat-spinner></mat-spinner> <mat-spinner></mat-spinner>
} @else { } @else {
@if (publication) { @if (publication(); as publication) {
<div class="card"> <div class="card">
<img src="/api/pictures/{{ publication.illustrationId }}" /> <img src="/api/pictures/{{ publication.illustrationId }}" />
<header> <header>
<h1>{{ publication.title }}</h1> <h1>{{ publication.title }}</h1>
<h2>{{ publication.description }}</h2> <h2>{{ publication.description }}</h2>
@if (isAuthorAndUserEquals) { @if (isAuthorAndUserEquals()) {
<a [routerLink]="['edit']" <a [routerLink]="['edit']"
class="button action" class="button action"
matTooltip="Click to edit the publication" matTooltip="Click to edit the publication"
@@ -32,7 +32,7 @@
@if (isAuthorAndUserEquals) { @if (isAuthorAndUserEquals) {
<button type="button" <button type="button"
(click)="deletePublication()" (click)="deletePublication()"
matTooltip="Click to delete the publication" matTooltip="Click to delete the publication"
matTooltipPosition="left" matTooltipPosition="left"
matRipple matRipple
i18n-matTooltip> i18n-matTooltip>
@@ -47,4 +47,4 @@
<h1 i18n>Publication failed to load...</h1> <h1 i18n>Publication failed to load...</h1>
</div> </div>
} }
} }

View File

@@ -1,5 +1,5 @@
import { CommonModule, Location } from '@angular/common'; import { CommonModule, Location } from '@angular/common';
import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import {Component, OnDestroy, OnInit, inject, signal} from '@angular/core';
import { MatRippleModule } from '@angular/material/core'; import { MatRippleModule } from '@angular/material/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatIcon } from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
@@ -37,9 +37,9 @@ export class PublicationComponent implements OnInit, OnDestroy {
private readonly snackBar = inject(MatSnackBar); private readonly snackBar = inject(MatSnackBar);
private paramMapSubscription?: Subscription; private paramMapSubscription?: Subscription;
private afterDialogCloseSubscription?: Subscription; private afterDialogCloseSubscription?: Subscription;
isLoading: boolean = false; isLoading = signal(false);
isAuthorAndUserEquals: boolean = false; isAuthorAndUserEquals = signal(false);
publication?: Publication; publication = signal<Publication | null>(null);
ngOnInit(): void { ngOnInit(): void {
this.paramMapSubscription = this.activatedRoute this.paramMapSubscription = this.activatedRoute
@@ -48,12 +48,12 @@ export class PublicationComponent implements OnInit, OnDestroy {
const publicationId = params.get('publicationId'); const publicationId = params.get('publicationId');
if (publicationId) { if (publicationId) {
this.isLoading = true; this.isLoading.set(true);
this.publicationRestService.getById(publicationId) this.publicationRestService.getById(publicationId)
.then(publication => { .then(publication => {
this.publication = publication; this.publication.set(publication);
this.isAuthorAndUserEquals = this.authenticationService.getAuthenticatedUser()?.id === this.publication.author.id; this.isAuthorAndUserEquals.set(this.authenticationService.getAuthenticatedUser()?.id === this.publication()?.author.id);
setTimeout(() => Prism.highlightAll(), 100); setTimeout(() => Prism.highlightAll(), 100);
}) })
.catch(error => { .catch(error => {
@@ -62,7 +62,7 @@ export class PublicationComponent implements OnInit, OnDestroy {
console.error(errorMessage, error); console.error(errorMessage, error);
}) })
.finally(() => { .finally(() => {
this.isLoading = false; this.isLoading.set(false);
}); });
} }
}); });
@@ -86,8 +86,9 @@ export class PublicationComponent implements OnInit, OnDestroy {
this.afterDialogCloseSubscription = dialogRef.afterClosed() this.afterDialogCloseSubscription = dialogRef.afterClosed()
.subscribe(response => { .subscribe(response => {
if (response && this.publication?.id) { const publication = this.publication();
this.publicationRestService.delete(this.publication.id); if (response && publication?.id) {
this.publicationRestService.delete(publication.id);
this.snackBar.open($localize`Publication deleted`, $localize`Close`, { duration: 5000 }); this.snackBar.open($localize`Publication deleted`, $localize`Close`, { duration: 5000 });
this.location.back(); this.location.back();
} }

View File

@@ -1,11 +1,12 @@
import { CommonModule } from "@angular/common"; import {CommonModule} from "@angular/common";
import { Component, inject, OnDestroy, OnInit } from "@angular/core"; import {Component, effect, inject} from "@angular/core";
import { MatProgressSpinner } from "@angular/material/progress-spinner"; import {MatProgressSpinner} from "@angular/material/progress-spinner";
import { ActivatedRoute } from "@angular/router"; import {ActivatedRoute} from "@angular/router";
import { Observable, Subscription } from "rxjs"; import {Observable} from "rxjs";
import { PublicationListComponent } from "../../components/publication-list/publication-list.component"; import {PublicationListComponent} from "../../components/publication-list/publication-list.component";
import { Publication } from "../../core/rest-services/publications/model/publication"; import {Publication} from "../../core/rest-services/publications/model/publication";
import { SearchPublicationsService } from "./search-publications.service"; import {SearchPublicationsService} from "./search-publications.service";
import {toSignal} from "@angular/core/rxjs-interop";
@Component({ @Component({
selector: 'app-search-publications', selector: 'app-search-publications',
@@ -14,27 +15,25 @@ import { SearchPublicationsService } from "./search-publications.service";
imports: [CommonModule, MatProgressSpinner, PublicationListComponent], imports: [CommonModule, MatProgressSpinner, PublicationListComponent],
providers: [SearchPublicationsService] providers: [SearchPublicationsService]
}) })
export class SearchPublicationsComponent implements OnInit, OnDestroy { export class SearchPublicationsComponent {
private searchPublicationsService = inject(SearchPublicationsService); private searchPublicationsService = inject(SearchPublicationsService);
private activatedRoute = inject(ActivatedRoute); private activatedRoute = inject(ActivatedRoute);
private queryParamsSubscription?: Subscription;
ngOnInit(): void { constructor() {
this.queryParamsSubscription = this.activatedRoute.queryParamMap const queryParams = toSignal(this.activatedRoute.queryParamMap);
.subscribe(params => { effect(() => {
const categoryId = params.get('category-id'); let params = queryParams();
if (categoryId) { if (params) {
this.searchPublicationsService.loadPublications(`category_id=${categoryId}`); const categoryId = params.get('category-id');
} if (categoryId) {
const query = params.get('query') this.searchPublicationsService.loadPublications(`category_id=${categoryId}`);
if (query) { }
this.searchPublicationsService.loadPublications(query); const query = params.get('query')
} if (query) {
}); this.searchPublicationsService.loadPublications(query);
} }
}
ngOnDestroy(): void { });
this.queryParamsSubscription?.unsubscribe();
} }
get publications$(): Observable<Publication[]> { get publications$(): Observable<Publication[]> {
@@ -48,4 +47,4 @@ export class SearchPublicationsComponent implements OnInit, OnDestroy {
get isLoaded$(): Observable<boolean> { get isLoaded$(): Observable<boolean> {
return this.searchPublicationsService.isLoaded$; return this.searchPublicationsService.isLoaded$;
} }
} }

View File

@@ -4,7 +4,6 @@ import { BehaviorSubject, Observable } from "rxjs";
import { Publication } from "../../core/rest-services/publications/model/publication"; import { Publication } from "../../core/rest-services/publications/model/publication";
import { MatSnackBar } from "@angular/material/snack-bar"; import { MatSnackBar } from "@angular/material/snack-bar";
@Injectable() @Injectable()
export class SearchPublicationsService { export class SearchPublicationsService {
private publicationRestService = inject(PublicationRestService); private publicationRestService = inject(PublicationRestService);
@@ -16,7 +15,7 @@ export class SearchPublicationsService {
get publications$(): Observable<Publication[]> { get publications$(): Observable<Publication[]> {
return this.publicationsSubject.asObservable(); return this.publicationsSubject.asObservable();
} }
get isLoading$(): Observable<boolean> { get isLoading$(): Observable<boolean> {
return this.isLoadingSubject.asObservable(); return this.isLoadingSubject.asObservable();
} }
@@ -46,4 +45,4 @@ export class SearchPublicationsService {
this.isLoadedSubject.next(true); this.isLoadedSubject.next(true);
}); });
} }
} }