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();
+ })
+ );
+ }
+}