From bacbdaec4205c086324a066ea20f4a3d567ee9cb Mon Sep 17 00:00:00 2001 From: maobaid Date: Thu, 24 Apr 2025 15:53:23 +0300 Subject: [PATCH] done with fetching pets and pet details parts --- src/app/app.config.ts | 3 +- .../add-pet-form/add-pet-form.component.ts | 8 ++++ .../pets-list/pets-list.component.ts | 5 ++- .../pet-details/pet-details.component.html | 18 ++++++--- .../pet-details/pet-details.component.ts | 31 ++++++++------- src/app/pages/pets/pets.component.html | 2 +- src/app/pages/pets/pets.component.ts | 22 ++++++----- src/app/shared/services/base.service.spec.ts | 16 ++++++++ src/app/shared/services/base.service.ts | 39 +++++++++++++++++++ src/app/shared/services/pets.service.spec.ts | 16 ++++++++ src/app/shared/services/pets.service.ts | 38 ++++++++++++++++++ 11 files changed, 163 insertions(+), 35 deletions(-) create mode 100644 src/app/shared/services/base.service.spec.ts create mode 100644 src/app/shared/services/base.service.ts create mode 100644 src/app/shared/services/pets.service.spec.ts create mode 100644 src/app/shared/services/pets.service.ts diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 6c6ef60..8bc4d99 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -2,7 +2,8 @@ import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; +import { provideHttpClient } from '@angular/common/http'; export const appConfig: ApplicationConfig = { - providers: [provideRouter(routes)] + providers: [provideRouter(routes), provideHttpClient()], }; diff --git a/src/app/components/add-pet-form/add-pet-form.component.ts b/src/app/components/add-pet-form/add-pet-form.component.ts index ef653ec..9a82dac 100644 --- a/src/app/components/add-pet-form/add-pet-form.component.ts +++ b/src/app/components/add-pet-form/add-pet-form.component.ts @@ -2,6 +2,9 @@ import { Component, inject } from '@angular/core'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; import { ModalService } from '../../shared/services/modal.service'; import { CommonModule } from '@angular/common'; +import { PetsService } from '../../shared/services/pets.service'; +import { pipe } from 'rxjs'; +import { Pet } from '../../../data/pets'; @Component({ selector: 'app-add-pet-form', @@ -13,6 +16,7 @@ import { CommonModule } from '@angular/common'; export class AddPetFormComponent { private fb = inject(FormBuilder); private modalService = inject(ModalService); + private petsService = inject(PetsService); petForm = this.fb.group({ name: ['', Validators.required], @@ -24,6 +28,10 @@ export class AddPetFormComponent { handleSubmit() { if (this.petForm.valid) { console.log('Submitted pet:', this.petForm.value); + const pet = this.petForm.value; + // this.petsService.addPet(pet).subscribe((savedPet) => { + // this.modalService.close(); + // }); this.modalService.close(); } else { console.log('Form is invalid'); diff --git a/src/app/components/pets-list/pets-list.component.ts b/src/app/components/pets-list/pets-list.component.ts index 3ada838..39b1784 100644 --- a/src/app/components/pets-list/pets-list.component.ts +++ b/src/app/components/pets-list/pets-list.component.ts @@ -1,13 +1,14 @@ -import { Component, Input } from '@angular/core'; +import { Component, effect, inject, Input, signal } from '@angular/core'; import { Pet } from '../../../data/pets'; import { PetCardComponent } from '../pet-card/pet-card.component'; +import { PetsService } from '../../shared/services/pets.service'; @Component({ selector: 'app-pets-list', standalone: true, imports: [PetCardComponent], templateUrl: './pets-list.component.html', - styleUrl: './pets-list.component.css' + styleUrl: './pets-list.component.css', }) export class PetsListComponent { @Input() pets: Pet[] = []; diff --git a/src/app/pages/pet-details/pet-details.component.html b/src/app/pages/pet-details/pet-details.component.html index 689c304..afff17f 100644 --- a/src/app/pages/pet-details/pet-details.component.html +++ b/src/app/pages/pet-details/pet-details.component.html @@ -2,19 +2,20 @@
+ @if(pet()){
-

Name: {{ pet?.name }}

-

Type: {{ pet?.type }}

-

Adopted: {{ pet?.adopted ? "yes" : "no" }}

+

Name: {{ pet()?.name }}

+

Type: {{ pet()?.type }}

+

Adopted: {{ pet()?.adopted ? "yes" : "no" }}

- @if (!pet?.adopted) { + @if (!pet()?.adopted) {
+ } @else { +
+

Pet not found. It may have been removed or never existed.

+
+ }
diff --git a/src/app/pages/pet-details/pet-details.component.ts b/src/app/pages/pet-details/pet-details.component.ts index 7159e7b..5d51edf 100644 --- a/src/app/pages/pet-details/pet-details.component.ts +++ b/src/app/pages/pet-details/pet-details.component.ts @@ -1,26 +1,27 @@ -import { Component } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { Pet, pets } from '../../../data/pets'; +import { Component, computed, inject } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { PetsService } from '../../shared/services/pets.service'; +import { map, switchMap } from 'rxjs'; @Component({ selector: 'app-pet-details', standalone: true, imports: [], templateUrl: './pet-details.component.html', - styleUrl: './pet-details.component.css' + styleUrl: './pet-details.component.css', }) export class PetDetailsComponent { - pet: Pet | null = null; - pets = pets; + private petsService = inject(PetsService); + private route = inject(ActivatedRoute); - constructor(private route: ActivatedRoute, private router: Router) { - const id = Number(this.route.snapshot.paramMap.get('id')); - const foundPet = pets.find((p) => p.id === id); + readonly id = computed(() => Number(this.route.snapshot.paramMap.get('id'))); - if (!foundPet) { - this.router.navigate(['/pets']); - } else { - this.pet = foundPet; - } - } + readonly pet = toSignal( + this.route.paramMap.pipe( + map((params) => Number(params.get('id'))), + switchMap((id) => this.petsService.getPet(id)) + ), + { initialValue: undefined } + ); } diff --git a/src/app/pages/pets/pets.component.html b/src/app/pages/pets/pets.component.html index 645e370..10e033f 100644 --- a/src/app/pages/pets/pets.component.html +++ b/src/app/pages/pets/pets.component.html @@ -1,4 +1,4 @@
- +
diff --git a/src/app/pages/pets/pets.component.ts b/src/app/pages/pets/pets.component.ts index 5628fb3..be37a3e 100644 --- a/src/app/pages/pets/pets.component.ts +++ b/src/app/pages/pets/pets.component.ts @@ -1,7 +1,8 @@ -import { Component } from '@angular/core'; +import { Component, computed, effect, inject, signal } from '@angular/core'; import { PetsHeaderComponent } from '../../components/pets-header/pets-header.component'; import { PetsListComponent } from '../../components/pets-list/pets-list.component'; -import { pets } from '../../../data/pets'; +import { PetsService } from '../../shared/services/pets.service'; +import { toSignal } from '@angular/core/rxjs-interop'; @Component({ selector: 'app-pets', @@ -11,16 +12,17 @@ import { pets } from '../../../data/pets'; styleUrl: './pets.component.css', }) export class PetsComponent { - query = ''; - allPets = pets; + readonly query = signal(''); + private petsService = inject(PetsService); + + readonly allPets = toSignal(this.petsService.getPets(), { initialValue: [] }); setQuery(query: string) { - this.query = query; + this.query.set(query); } - get filteredPets() { - return this.allPets.filter((pet) => - pet.name.toLowerCase().includes(this.query.toLowerCase()) - ); - } + readonly filteredPets = computed(() => { + const q = this.query().toLowerCase(); + return this.allPets().filter((pet) => pet.name.toLowerCase().includes(q)); + }); } diff --git a/src/app/shared/services/base.service.spec.ts b/src/app/shared/services/base.service.spec.ts new file mode 100644 index 0000000..452c7f7 --- /dev/null +++ b/src/app/shared/services/base.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { BaseService } from './base.service'; + +describe('BaseService', () => { + let service: BaseService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(BaseService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/shared/services/base.service.ts b/src/app/shared/services/base.service.ts new file mode 100644 index 0000000..35ead8e --- /dev/null +++ b/src/app/shared/services/base.service.ts @@ -0,0 +1,39 @@ +import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { inject, Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root', +}) +export class BaseService { + // ✅ Angular 17 way: use inject() instead of constructor injection + // constructor(private readonly _http: HttpClient) {} + private readonly _http = inject(HttpClient); + + /** + * Generic GET method to fetch data + */ + get(url: string, params?: HttpParams, headers?: HttpHeaders) { + return this._http.get(url, { params, headers }); + } + + /** + * Generic POST method to add new data + */ + post(url: string, body: T, headers?: HttpHeaders) { + return this._http.post(url, body, { headers }); + } + + /** + * Generic PUT method to update data + */ + put(url: string, body: T, headers?: HttpHeaders) { + return this._http.put(url, body, { headers }); + } + + /** + * Generic DELETE method to remove data + */ + delete(url: string, params?: HttpParams, headers?: HttpHeaders) { + return this._http.delete(url, { params, headers }); + } +} diff --git a/src/app/shared/services/pets.service.spec.ts b/src/app/shared/services/pets.service.spec.ts new file mode 100644 index 0000000..26fc85f --- /dev/null +++ b/src/app/shared/services/pets.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { PetsService } from './pets.service'; + +describe('PetsService', () => { + let service: PetsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PetsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/shared/services/pets.service.ts b/src/app/shared/services/pets.service.ts new file mode 100644 index 0000000..cb6e59c --- /dev/null +++ b/src/app/shared/services/pets.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@angular/core'; +import { catchError, Observable, of } from 'rxjs'; +import { Pet } from '../../../data/pets'; +import { BaseService } from './base.service'; + +@Injectable({ + providedIn: 'root', +}) +export class PetsService extends BaseService { + private apiUrl = 'https://pets-react-query-backend.eapi.joincoded.com/pets'; + + getPets(): Observable { + return this.get(this.apiUrl).pipe( + catchError((error) => { + console.error('Error fetching pets:', error); + return of([]); + }) + ); + } + + getPet(id: number): Observable { + return this.get(this.apiUrl + '/' + id).pipe( + catchError((error) => { + console.error('Error fetching pet:', error); + return of(); + }) + ); + } + + addPet(pet: Pet): Observable { + return this.post(this.apiUrl, pet).pipe( + catchError((error) => { + console.error('Error adding pet:', error); + return of(); + }) + ); + } +}