diff --git a/libs/ngrx-hateoas/src/lib/store-features/with-hypermedia-resource.spec.ts b/libs/ngrx-hateoas/src/lib/store-features/with-hypermedia-resource.spec.ts index 0b92dcb..41b8dda 100644 --- a/libs/ngrx-hateoas/src/lib/store-features/with-hypermedia-resource.spec.ts +++ b/libs/ngrx-hateoas/src/lib/store-features/with-hypermedia-resource.spec.ts @@ -376,6 +376,36 @@ describe('withHypermediaResource', () => { httpTestingController.verify(); }); + it('resets state when url signal changes to null after being loaded', (done: DoneFn) => { + const resourceFirst: TestModel = { + numProp: 2, + objProp: { stringProp: 'first response' } + }; + + const urlSignal = signal('api/test-model'); + store.loadTestModelFromUrl(urlSignal); + + TestBed.flushEffects(); + + httpTestingController.expectOne('api/test-model').flush(resourceFirst); + + setTimeout(() => { + expect(store.testModelState.isLoaded()).toBeTrue(); + expect(store.testModel.objProp.stringProp()).toBe('first response'); + + urlSignal.set(null); + TestBed.flushEffects(); + + expect(store.testModelState.url()).toBe(''); + expect(store.testModelState.isLoaded()).toBeFalse(); + expect(store.testModelState.isLoading()).toBeFalse(); + expect(store.testModel()).toBe(initialTestModel); + + httpTestingController.verify(); + done(); + }, 0); + }); + it('loads the resource reactively from a url signal and reloads when signal changes', (done: DoneFn) => { const resourceFirst: TestModel = { numProp: 2, diff --git a/libs/ngrx-hateoas/src/lib/store-features/with-linked-hypermedia-resource.spec.ts b/libs/ngrx-hateoas/src/lib/store-features/with-linked-hypermedia-resource.spec.ts index dd0827a..d13726e 100644 --- a/libs/ngrx-hateoas/src/lib/store-features/with-linked-hypermedia-resource.spec.ts +++ b/libs/ngrx-hateoas/src/lib/store-features/with-linked-hypermedia-resource.spec.ts @@ -230,4 +230,50 @@ describe('withLinkedHypermediaResource', () => { httpTestingController.verify(); }); + it('resets state when link disappears from root resource after being loaded', async () => { + const rootModelFromUrl: RootModel = { + apiName: 'loaded model', + _links: { + testModel: { href: '/api/test-model' } + } + }; + + const testModelFromLink: TestModel = { + name: 'from link' + }; + + const loadRootModel = store.loadRootModelFromUrl('/api/root-model'); + + httpTestingController.expectOne('/api/root-model').flush(rootModelFromUrl); + httpTestingController.verify(); + + await loadRootModel; + await firstValueFrom(timer(0)); + + httpTestingController.expectOne('/api/test-model').flush(testModelFromLink); + httpTestingController.verify(); + + await firstValueFrom(timer(0)); + + expect(store.testModelState.isLoaded()).toBeTrue(); + expect(store.testModelState.isAvailable()).toBeTrue(); + expect(store.testModel.name()).toBe('from link'); + + // Reload root model without the link + const reloadRootModel = store.reloadRootModel(); + + httpTestingController.expectOne('/api/root-model').flush({ apiName: 'loaded model', _links: {} } satisfies RootModel); + httpTestingController.verify(); + + await reloadRootModel; + await firstValueFrom(timer(0)); + + expect(store.testModelState.isLoaded()).toBeFalse(); + expect(store.testModelState.isAvailable()).toBeFalse(); + expect(store.testModelState.isLoading()).toBeFalse(); + expect(store.testModel()).toBe(initialTestModel); + + httpTestingController.verify(); + }); + }); diff --git a/libs/ngrx-hateoas/src/lib/store-features/with-linked-hypermedia-resource.ts b/libs/ngrx-hateoas/src/lib/store-features/with-linked-hypermedia-resource.ts index a1d6905..479993a 100644 --- a/libs/ngrx-hateoas/src/lib/store-features/with-linked-hypermedia-resource.ts +++ b/libs/ngrx-hateoas/src/lib/store-features/with-linked-hypermedia-resource.ts @@ -153,6 +153,13 @@ export function withLinkedHypermediaResource resource !== undefined), map(resource => hateoasService.getLink(resource, linkMetaName)?.href), + tap(href => { + if (!isValidHref(href)) { + patchState(store, + updateData(dataKey, initialValue), + updateState(stateKey, { url: '', isLoading: false, isLoaded: false, isAvailable: false })); + } + }), filter(href => isValidHref(href)), map(href => href!), filter(href => getState(store, stateKey).url !== href),