From 6fd952413f7ca2634eb667e2c01928ac80e063b3 Mon Sep 17 00:00:00 2001 From: Michael Small Date: Mon, 26 Aug 2024 19:51:16 -0500 Subject: [PATCH 1/7] refactor: add observable/subscription types to `withDataService` interface and method signatures --- .../ngrx-toolkit/src/lib/with-data-service.ts | 55 ++++++++++++------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/libs/ngrx-toolkit/src/lib/with-data-service.ts b/libs/ngrx-toolkit/src/lib/with-data-service.ts index a8ff62a..14fac45 100644 --- a/libs/ngrx-toolkit/src/lib/with-data-service.ts +++ b/libs/ngrx-toolkit/src/lib/with-data-service.ts @@ -24,22 +24,39 @@ import { removeEntity, } from '@ngrx/signals/entities'; import { EntityState, NamedEntityComputed } from './shared/signal-store-models'; +import { Observable, isObservable, Subscription } from 'rxjs'; +import {HttpErrorResponse} from "@angular/common/http"; +import {tapResponse} from "@ngrx/operators"; export type Filter = Record; export type Entity = { id: EntityId }; -export interface DataService { - load(filter: F): Promise; +// Observable checking handled by rxjs/isObservable +function isPromise(value: PromiseOrObservable): value is Promise { + return value && typeof (value as Promise).then === 'function'; +} + +// For interface +type PromiseOrObservable = Promise | Observable; +// For methods +// TODO - will likely be an unsubscription after working these things out - the unsub strategy is outstanding for now +type PromiseOrSubscription = Promise | Subscription; + +export interface DataService< + E extends Entity, + F extends Filter> +{ + load(filter: F): PromiseOrObservable; - loadById(id: EntityId): Promise; + loadById(id: EntityId): PromiseOrObservable; - create(entity: E): Promise; + create(entity: E): PromiseOrObservable; - update(entity: E): Promise; + update(entity: E): PromiseOrObservable; - updateAll(entity: E[]): Promise; + updateAll(entity: E[]): PromiseOrObservable; - delete(entity: E): Promise; + delete(entity: E): PromiseOrObservable ; } export function capitalize(str: string): string { @@ -160,36 +177,36 @@ export type NamedDataServiceMethods< selected: boolean ) => void; } & { - [K in Collection as `load${Capitalize}Entities`]: () => Promise; + [K in Collection as `load${Capitalize}Entities`]: () => PromiseOrSubscription; } & { [K in Collection as `setCurrent${Capitalize}`]: (entity: E) => void; } & { [K in Collection as `load${Capitalize}ById`]: ( id: EntityId - ) => Promise; + ) => PromiseOrSubscription; } & { - [K in Collection as `create${Capitalize}`]: (entity: E) => Promise; + [K in Collection as `create${Capitalize}`]: (entity: E) => PromiseOrSubscription; } & { - [K in Collection as `update${Capitalize}`]: (entity: E) => Promise; + [K in Collection as `update${Capitalize}`]: (entity: E) => PromiseOrSubscription; } & { [K in Collection as `updateAll${Capitalize}`]: ( entity: E[] - ) => Promise; + ) => PromiseOrSubscription; } & { - [K in Collection as `delete${Capitalize}`]: (entity: E) => Promise; + [K in Collection as `delete${Capitalize}`]: (entity: E) => PromiseOrSubscription; }; export type DataServiceMethods = { updateFilter: (filter: F) => void; updateSelected: (id: EntityId, selected: boolean) => void; - load: () => Promise; + load: () => PromiseOrSubscription; setCurrent(entity: E): void; - loadById(id: EntityId): Promise; - create(entity: E): Promise; - update(entity: E): Promise; - updateAll(entities: E[]): Promise; - delete(entity: E): Promise; + loadById(id: EntityId): PromiseOrSubscription; + create(entity: E): PromiseOrSubscription; + update(entity: E): PromiseOrSubscription; + updateAll(entities: E[]): PromiseOrSubscription; + delete(entity: E): PromiseOrSubscription; }; export function withDataService< From e558db46ed4b1aabbf5627cd1812da2c2149e8fa Mon Sep 17 00:00:00 2001 From: Michael Small Date: Mon, 26 Aug 2024 19:55:25 -0500 Subject: [PATCH 2/7] refactor: convert `load` of `withDataService` to handle Observable --- .../ngrx-toolkit/src/lib/with-data-service.ts | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/libs/ngrx-toolkit/src/lib/with-data-service.ts b/libs/ngrx-toolkit/src/lib/with-data-service.ts index 14fac45..cada7f8 100644 --- a/libs/ngrx-toolkit/src/lib/with-data-service.ts +++ b/libs/ngrx-toolkit/src/lib/with-data-service.ts @@ -304,22 +304,33 @@ export function withDataService< }, })); }, - [loadKey]: async (): Promise => { + [loadKey]: (): PromiseOrSubscription => { const filter = store[filterKey] as Signal; store[callStateKey] && patchState(store, setLoading(prefix)); - - try { - const result = await dataService.load(filter()); - patchState( - store, - prefix - ? setAllEntities(result, { collection: prefix }) - : setAllEntities(result) - ); - store[callStateKey] && patchState(store, setLoaded(prefix)); - } catch (e) { - store[callStateKey] && patchState(store, setError(e, prefix)); - throw e; + const serviceCall = dataService.load(filter()); + + if (isObservable(serviceCall)) { + return serviceCall.pipe( + tapResponse((result) => { + patchState( + store, + prefix + ? setAllEntities(result, { collection: prefix }) + : setAllEntities(result)); + store[callStateKey] && patchState(store, setLoaded(prefix)); + }, (errorResponse: HttpErrorResponse) => store[callStateKey] && patchState(store, setError(errorResponse, prefix)))).subscribe(); + } else { + const loadPromise = async () => { + try { + const result = await serviceCall; + patchState(store, prefix ? setAllEntities(result, { collection: prefix }) : setAllEntities(result)); + store[callStateKey] && patchState(store, setLoaded(prefix)); + } catch (e) { + store[callStateKey] && patchState(store, setError(e, prefix)); + throw e; + } + }; + return loadPromise(); } }, [loadByIdKey]: async (id: EntityId): Promise => { From 02427a4765088c154dec2960af9d3e26e238bd88 Mon Sep 17 00:00:00 2001 From: Michael Small Date: Mon, 26 Aug 2024 20:29:58 -0500 Subject: [PATCH 3/7] refactor: add RXJS version of `withDataService` for methods `loadById/create/update/updateAll/delete`key --- .../ngrx-toolkit/src/lib/with-data-service.ts | 241 +++++++++++++----- 1 file changed, 173 insertions(+), 68 deletions(-) diff --git a/libs/ngrx-toolkit/src/lib/with-data-service.ts b/libs/ngrx-toolkit/src/lib/with-data-service.ts index cada7f8..e6943cb 100644 --- a/libs/ngrx-toolkit/src/lib/with-data-service.ts +++ b/libs/ngrx-toolkit/src/lib/with-data-service.ts @@ -333,100 +333,205 @@ export function withDataService< return loadPromise(); } }, - [loadByIdKey]: async (id: EntityId): Promise => { + [loadByIdKey]: (id: EntityId): PromiseOrSubscription => { store[callStateKey] && patchState(store, setLoading(prefix)); - try { - const current = await dataService.loadById(id); - store[callStateKey] && patchState(store, setLoaded(prefix)); - patchState(store, { [currentKey]: current }); - } catch (e) { - store[callStateKey] && patchState(store, setError(e, prefix)); - throw e; + const serviceCall = dataService.loadById(id); + + if (isObservable(serviceCall)) { + return serviceCall.pipe( + tapResponse( + (current) => { + store[callStateKey] && patchState(store, setLoaded(prefix)); + patchState(store, { [currentKey]: current }); + }, (errorResponse: HttpErrorResponse) => store[callStateKey] && patchState(store, setError(errorResponse, prefix)) + ) + ).subscribe() + } else { + const loadByIdPromise = async () => { + try { + const current = await dataService.loadById(id); + store[callStateKey] && patchState(store, setLoaded(prefix)); + patchState(store, { [currentKey]: current }); + } catch (e) { + store[callStateKey] && patchState(store, setError(e, prefix)); + throw e; + } + } + return loadByIdPromise() } + }, [setCurrentKey]: (current: E): void => { patchState(store, { [currentKey]: current }); }, - [createKey]: async (entity: E): Promise => { + [createKey]: (entity: E): PromiseOrSubscription => { patchState(store, { [currentKey]: entity }); store[callStateKey] && patchState(store, setLoading(prefix)); - try { - const created = await dataService.create(entity); - patchState(store, { [currentKey]: created }); - patchState( - store, - prefix - ? addEntity(created, { collection: prefix }) - : addEntity(created) - ); - store[callStateKey] && patchState(store, setLoaded(prefix)); - } catch (e) { - store[callStateKey] && patchState(store, setError(e, prefix)); - throw e; + const serviceCall = dataService.create(entity) + + if (isObservable(serviceCall)) { + return serviceCall.pipe( + tapResponse((created) => { + patchState(store, { [currentKey]: created }); + patchState( + store, + prefix + ? addEntity(created, { collection: prefix }) + : addEntity(created) + ); + store[callStateKey] && patchState(store, setLoaded(prefix)); + }, (errorResponse: HttpErrorResponse) => store[callStateKey] && patchState(store, setError(errorResponse, prefix))) + ).subscribe() + } else { + const createPromise = async () => { + try { + const created = await serviceCall; + patchState(store, { [currentKey]: created }); + patchState( + store, + prefix + ? addEntity(created, { collection: prefix }) + : addEntity(created) + ); + store[callStateKey] && patchState(store, setLoaded(prefix)); + } catch (e) { + store[callStateKey] && patchState(store, setError(e, prefix)); + throw e; + } + } + return createPromise() } }, - [updateKey]: async (entity: E): Promise => { + [updateKey]: (entity: E): PromiseOrSubscription => { patchState(store, { [currentKey]: entity }); store[callStateKey] && patchState(store, setLoading(prefix)); - try { - const updated = await dataService.update(entity); - patchState(store, { [currentKey]: updated }); + const serviceCall = dataService.update(entity); - const updateArg = { - id: updated.id, - changes: updated, - }; + if (isObservable(serviceCall)) { + return serviceCall.pipe( + tapResponse((updated) => { + patchState(store, { [currentKey]: updated }); + + const updateArg = { + id: updated.id, + changes: updated, + }; + + const updater = (collection: string) => + updateEntity(updateArg, { collection }); + + patchState( + store, + prefix ? updater(prefix) : updateEntity(updateArg) + ); + store[callStateKey] && patchState(store, setLoaded(prefix)); + }, (error: HttpErrorResponse) => store[callStateKey] && patchState(store, setError(error, prefix))) + ).subscribe() + } else { + const updatePromise = async() => { + try { + const updated = await serviceCall; + patchState(store, { [currentKey]: updated }); + + const updateArg = { + id: updated.id, + changes: updated, + }; + + const updater = (collection: string) => + updateEntity(updateArg, { collection }); - const updater = (collection: string) => - updateEntity(updateArg, { collection }); - - patchState( - store, - prefix ? updater(prefix) : updateEntity(updateArg) - ); - store[callStateKey] && patchState(store, setLoaded(prefix)); - } catch (e) { - store[callStateKey] && patchState(store, setError(e, prefix)); - throw e; + patchState( + store, + prefix ? updater(prefix) : updateEntity(updateArg) + ); + store[callStateKey] && patchState(store, setLoaded(prefix)); + } catch (e) { + store[callStateKey] && patchState(store, setError(e, prefix)); + throw e; + } + } + return updatePromise() } }, - [updateAllKey]: async (entities: E[]): Promise => { + [updateAllKey]: (entities: E[]): PromiseOrSubscription => { store[callStateKey] && patchState(store, setLoading(prefix)); - try { - const result = await dataService.updateAll(entities); - patchState( - store, - prefix - ? setAllEntities(result, { collection: prefix }) - : setAllEntities(result) - ); - store[callStateKey] && patchState(store, setLoaded(prefix)); - } catch (e) { - store[callStateKey] && patchState(store, setError(e, prefix)); - throw e; + const serviceCall = dataService.updateAll(entities); + if (isObservable(serviceCall)) { + return serviceCall.pipe( + tapResponse((result) => { + patchState( + store, + prefix + ? setAllEntities(result, { collection: prefix }) + : setAllEntities(result) + ); + store[callStateKey] && patchState(store, setLoaded(prefix)); + }, (error: HttpErrorResponse) => store[callStateKey] && patchState(store, setError(error, prefix))) + ).subscribe() + } else { + const updateAllPromise = async() => { + try { + const result = await serviceCall; + patchState( + store, + prefix + ? setAllEntities(result, { collection: prefix }) + : setAllEntities(result) + ); + store[callStateKey] && patchState(store, setLoaded(prefix)); + } catch (e) { + store[callStateKey] && patchState(store, setError(e, prefix)); + throw e; + } + } + return updateAllPromise() } }, - [deleteKey]: async (entity: E): Promise => { + [deleteKey]: (entity: E): PromiseOrSubscription => { patchState(store, { [currentKey]: entity }); store[callStateKey] && patchState(store, setLoading(prefix)); - try { - await dataService.delete(entity); - patchState(store, { [currentKey]: undefined }); - patchState( - store, - prefix - ? removeEntity(entity.id, { collection: prefix }) - : removeEntity(entity.id) - ); - store[callStateKey] && patchState(store, setLoaded(prefix)); - } catch (e) { - store[callStateKey] && patchState(store, setError(e, prefix)); - throw e; + const serviceCall = dataService.delete(entity) + + if (isObservable(serviceCall)) { + return serviceCall.pipe( + tapResponse( + (() => { + patchState(store, { [currentKey]: undefined }); + patchState( + store, + prefix + ? removeEntity(entity.id, { collection: prefix }) + : removeEntity(entity.id) + ); + store[callStateKey] && patchState(store, setLoaded(prefix)); + }), + (error: HttpErrorResponse) => store[callStateKey] && patchState(store, setError(error, prefix)), + ) + ).subscribe() + } else { + const deletePromise = async() => { + try { + await serviceCall; + patchState(store, { [currentKey]: undefined }); + patchState( + store, + prefix + ? removeEntity(entity.id, { collection: prefix }) + : removeEntity(entity.id) + ); + store[callStateKey] && patchState(store, setLoaded(prefix)); + } catch (e) { + store[callStateKey] && patchState(store, setError(e, prefix)); + throw e; + } + } + return deletePromise() } }, }; From 9461dc24dcea28ff6d7b684141169fc37ed5f78c Mon Sep 17 00:00:00 2001 From: Michael Small Date: Mon, 26 Aug 2024 20:49:17 -0500 Subject: [PATCH 4/7] chore: change import order and format of `withDataService` method consistently --- .../ngrx-toolkit/src/lib/with-data-service.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/libs/ngrx-toolkit/src/lib/with-data-service.ts b/libs/ngrx-toolkit/src/lib/with-data-service.ts index e6943cb..f67c38f 100644 --- a/libs/ngrx-toolkit/src/lib/with-data-service.ts +++ b/libs/ngrx-toolkit/src/lib/with-data-service.ts @@ -1,4 +1,5 @@ import { ProviderToken, Signal, computed, inject } from '@angular/core'; +import {HttpErrorResponse} from "@angular/common/http"; import { SignalStoreFeature, patchState, @@ -23,19 +24,13 @@ import { updateEntity, removeEntity, } from '@ngrx/signals/entities'; -import { EntityState, NamedEntityComputed } from './shared/signal-store-models'; -import { Observable, isObservable, Subscription } from 'rxjs'; -import {HttpErrorResponse} from "@angular/common/http"; import {tapResponse} from "@ngrx/operators"; +import { Observable, isObservable, Subscription } from 'rxjs'; +import { EntityState, NamedEntityComputed } from './shared/signal-store-models'; export type Filter = Record; export type Entity = { id: EntityId }; -// Observable checking handled by rxjs/isObservable -function isPromise(value: PromiseOrObservable): value is Promise { - return value && typeof (value as Promise).then === 'function'; -} - // For interface type PromiseOrObservable = Promise | Observable; // For methods @@ -307,6 +302,7 @@ export function withDataService< [loadKey]: (): PromiseOrSubscription => { const filter = store[filterKey] as Signal; store[callStateKey] && patchState(store, setLoading(prefix)); + const serviceCall = dataService.load(filter()); if (isObservable(serviceCall)) { @@ -318,12 +314,16 @@ export function withDataService< ? setAllEntities(result, { collection: prefix }) : setAllEntities(result)); store[callStateKey] && patchState(store, setLoaded(prefix)); - }, (errorResponse: HttpErrorResponse) => store[callStateKey] && patchState(store, setError(errorResponse, prefix)))).subscribe(); + }, (errorResponse: HttpErrorResponse) => store[callStateKey] && patchState(store, setError(errorResponse, prefix)))).subscribe(); } else { const loadPromise = async () => { try { const result = await serviceCall; - patchState(store, prefix ? setAllEntities(result, { collection: prefix }) : setAllEntities(result)); + patchState( + store, + prefix + ? setAllEntities(result, { collection: prefix }) + : setAllEntities(result)); store[callStateKey] && patchState(store, setLoaded(prefix)); } catch (e) { store[callStateKey] && patchState(store, setError(e, prefix)); @@ -461,6 +461,7 @@ export function withDataService< store[callStateKey] && patchState(store, setLoading(prefix)); const serviceCall = dataService.updateAll(entities); + if (isObservable(serviceCall)) { return serviceCall.pipe( tapResponse((result) => { From 8d403da2b74ebe638982493dec902a67cc2081d2 Mon Sep 17 00:00:00 2001 From: Michael Small Date: Mon, 26 Aug 2024 21:02:16 -0500 Subject: [PATCH 5/7] chore: create and swap in Observable based `withDataService` example --- .../flight-booking-simple.store.ts | 15 +++++- .../flight-search-simple.component.ts | 5 +- apps/demo/src/app/shared/flight.service.ts | 50 ++++++++++++++++++- 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/apps/demo/src/app/flight-search-data-service-simple/flight-booking-simple.store.ts b/apps/demo/src/app/flight-search-data-service-simple/flight-booking-simple.store.ts index b7a98c8..607f8f3 100644 --- a/apps/demo/src/app/flight-search-data-service-simple/flight-booking-simple.store.ts +++ b/apps/demo/src/app/flight-search-data-service-simple/flight-booking-simple.store.ts @@ -13,8 +13,19 @@ export const SimpleFlightBookingStore = signalStore( withCallState(), withEntities(), withDataService({ - dataServiceType: FlightService, + dataServiceType: FlightService, filter: { from: 'Paris', to: 'New York' }, }), withUndoRedo(), -); \ No newline at end of file +); + +export const SimpleFlightBookingStoreWithObservables = signalStore( + { providedIn: 'root' }, + withCallState(), + withEntities(), + withDataService({ + dataServiceType: FlightService, + filter: { from: 'Paris', to: 'New York' }, + }), + withUndoRedo(), +); diff --git a/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.ts b/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.ts index be55ee6..95edb76 100644 --- a/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.ts +++ b/apps/demo/src/app/flight-search-data-service-simple/flight-search-simple.component.ts @@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms'; import { ChangeDetectionStrategy } from '@angular/core'; import { RouterLink } from '@angular/router'; import { FlightCardComponent } from '../shared/flight-card.component'; -import { SimpleFlightBookingStore } from './flight-booking-simple.store'; +import { SimpleFlightBookingStore, SimpleFlightBookingStoreWithObservables } from './flight-booking-simple.store'; import { MatTableModule } from '@angular/material/table'; import { MatInputModule } from '@angular/material/input'; import { MatButtonModule } from '@angular/material/button'; @@ -31,7 +31,8 @@ import { MatButtonModule } from '@angular/material/button'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class FlightSearchSimpleComponent { - private store = inject(SimpleFlightBookingStore); + // private store = inject(SimpleFlightBookingStore); + private store = inject(SimpleFlightBookingStoreWithObservables); // TODO - differentiate these in more nuanced way than comment in/out from = this.store.filter.from; to = this.store.filter.to; diff --git a/apps/demo/src/app/shared/flight.service.ts b/apps/demo/src/app/shared/flight.service.ts index 165222e..cab29c3 100644 --- a/apps/demo/src/app/shared/flight.service.ts +++ b/apps/demo/src/app/shared/flight.service.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Observable, firstValueFrom } from 'rxjs'; +import { inject, Injectable } from '@angular/core'; +import { Observable, firstValueFrom, catchError, of } from 'rxjs'; import { EntityId } from '@ngrx/signals/entities'; import { DataService } from 'ngrx-toolkit'; import { Flight } from './flight'; @@ -77,3 +77,49 @@ export class FlightService implements DataService { } } + +@Injectable({ + providedIn: 'root' +}) +export class FlightServiceRXJS implements DataService { + private url = 'http://localhost:3000/books'; + private http = inject(HttpClient); + + loadById(id: EntityId): Observable { + const reqObj = { params: new HttpParams().set('id', id) }; + return this.http + .get(this.url, reqObj) + .pipe(catchError((_) => of())); + } + + create(entity: Flight): Observable { + return this.http + .post(this.url, entity) + .pipe(catchError((_) => of())); + } + + update(entity: Flight): Observable { + return this.http + .post(this.url, entity) + .pipe(catchError((_) => of())); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + updateAll(entity: Flight[]): Observable { + throw new Error('updateAll method not implemented.'); + } + + delete(entity: Flight): Observable { + return this.http + .delete(`${this.url}/${entity.id}`) + .pipe(catchError((_) => of())); + } + + load(filter: FlightFilter): Observable { + console.log('loading'); + // TODO - actually add in filter + return this.http + .get(this.url) + .pipe(catchError((_) => of([]))); + } +} From 191864f89126998aa13c45e3e421f587c7cebe69 Mon Sep 17 00:00:00 2001 From: Michael Small Date: Mon, 26 Aug 2024 21:13:18 -0500 Subject: [PATCH 6/7] chore: make RXJS `withDataService` implementation closer to promise version --- apps/demo/src/app/shared/flight.service.ts | 55 ++++++++++++++++------ 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/apps/demo/src/app/shared/flight.service.ts b/apps/demo/src/app/shared/flight.service.ts index cab29c3..d9502c0 100644 --- a/apps/demo/src/app/shared/flight.service.ts +++ b/apps/demo/src/app/shared/flight.service.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; -import { Observable, firstValueFrom, catchError, of } from 'rxjs'; +import { Observable, firstValueFrom, catchError, of, pipe } from 'rxjs'; import { EntityId } from '@ngrx/signals/entities'; import { DataService } from 'ngrx-toolkit'; import { Flight } from './flight'; @@ -82,25 +82,22 @@ export class FlightService implements DataService { providedIn: 'root' }) export class FlightServiceRXJS implements DataService { - private url = 'http://localhost:3000/books'; + private baseUrl = 'http://localhost:3000/books'; private http = inject(HttpClient); loadById(id: EntityId): Observable { - const reqObj = { params: new HttpParams().set('id', id) }; - return this.http - .get(this.url, reqObj) + return this.findById('' + id) .pipe(catchError((_) => of())); } create(entity: Flight): Observable { - return this.http - .post(this.url, entity) + entity.id = 0; + return this.save(entity) .pipe(catchError((_) => of())); } update(entity: Flight): Observable { - return this.http - .post(this.url, entity) + return this.save(entity) .pipe(catchError((_) => of())); } @@ -110,16 +107,44 @@ export class FlightServiceRXJS implements DataService { } delete(entity: Flight): Observable { - return this.http - .delete(`${this.url}/${entity.id}`) + return this.remove(entity) .pipe(catchError((_) => of())); } load(filter: FlightFilter): Observable { - console.log('loading'); - // TODO - actually add in filter - return this.http - .get(this.url) + return this.find(filter.from, filter.to) .pipe(catchError((_) => of([]))); } + + private find( + from: string, + to: string, + urgent = false + ): Observable { + let url = [this.baseUrl, 'flight'].join('/'); + + if (urgent) { + url = [this.baseUrl, 'error?code=403'].join('/'); + } + + const params = new HttpParams().set('from', from).set('to', to); + const headers = new HttpHeaders().set('Accept', 'application/json'); + return this.http.get(url, { params, headers }); + } + + private findById(id: string): Observable { + const reqObj = { params: new HttpParams().set('id', id) }; + const url = [this.baseUrl, 'flight'].join('/'); + return this.http.get(url, reqObj); + } + + private save(flight: Flight): Observable { + const url = [this.baseUrl, 'flight'].join('/'); + return this.http.post(url, flight); + } + + private remove(flight: Flight): Observable { + const url = [this.baseUrl, 'flight', flight.id].join('/'); + return this.http.delete(url); + } } From 47fc853d373905dd81c267ecc8ac972e071350f8 Mon Sep 17 00:00:00 2001 From: Michael Small Date: Mon, 26 Aug 2024 22:59:30 -0500 Subject: [PATCH 7/7] fix: point service at actual endpoint/service, store at actual service for RXJS `withDataService` --- .../flight-booking-simple.store.ts | 4 ++-- apps/demo/src/app/shared/flight.service.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/demo/src/app/flight-search-data-service-simple/flight-booking-simple.store.ts b/apps/demo/src/app/flight-search-data-service-simple/flight-booking-simple.store.ts index 6b537f6..edcdfe2 100644 --- a/apps/demo/src/app/flight-search-data-service-simple/flight-booking-simple.store.ts +++ b/apps/demo/src/app/flight-search-data-service-simple/flight-booking-simple.store.ts @@ -1,4 +1,4 @@ -import { FlightService } from '../shared/flight.service'; +import { FlightService, FlightServiceRXJS } from '../shared/flight.service'; import { signalStore } from '@ngrx/signals'; @@ -26,7 +26,7 @@ export const SimpleFlightBookingStoreWithObservables = signalStore( withCallState(), withEntities(), withDataService({ - dataServiceType: FlightService, + dataServiceType: FlightServiceRXJS, filter: { from: 'Paris', to: 'New York' }, }), withUndoRedo(), diff --git a/apps/demo/src/app/shared/flight.service.ts b/apps/demo/src/app/shared/flight.service.ts index dfce2b5..7552f30 100644 --- a/apps/demo/src/app/shared/flight.service.ts +++ b/apps/demo/src/app/shared/flight.service.ts @@ -77,7 +77,7 @@ export class FlightService implements DataService { providedIn: 'root' }) export class FlightServiceRXJS implements DataService { - private baseUrl = 'http://localhost:3000/books'; + private baseUrl = `https://demo.angulararchitects.io/api`; private http = inject(HttpClient); loadById(id: EntityId): Observable {