Skip to content

Commit 0334d5a

Browse files
Fix of edge case state handling
1 parent 941e65a commit 0334d5a

3 files changed

Lines changed: 83 additions & 0 deletions

File tree

libs/ngrx-hateoas/src/lib/store-features/with-hypermedia-resource.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,36 @@ describe('withHypermediaResource', () => {
376376
httpTestingController.verify();
377377
});
378378

379+
it('resets state when url signal changes to null after being loaded', (done: DoneFn) => {
380+
const resourceFirst: TestModel = {
381+
numProp: 2,
382+
objProp: { stringProp: 'first response' }
383+
};
384+
385+
const urlSignal = signal<string | null>('api/test-model');
386+
store.loadTestModelFromUrl(urlSignal);
387+
388+
TestBed.flushEffects();
389+
390+
httpTestingController.expectOne('api/test-model').flush(resourceFirst);
391+
392+
setTimeout(() => {
393+
expect(store.testModelState.isLoaded()).toBeTrue();
394+
expect(store.testModel.objProp.stringProp()).toBe('first response');
395+
396+
urlSignal.set(null);
397+
TestBed.flushEffects();
398+
399+
expect(store.testModelState.url()).toBe('');
400+
expect(store.testModelState.isLoaded()).toBeFalse();
401+
expect(store.testModelState.isLoading()).toBeFalse();
402+
expect(store.testModel()).toBe(initialTestModel);
403+
404+
httpTestingController.verify();
405+
done();
406+
}, 0);
407+
});
408+
379409
it('loads the resource reactively from a url signal and reloads when signal changes', (done: DoneFn) => {
380410
const resourceFirst: TestModel = {
381411
numProp: 2,

libs/ngrx-hateoas/src/lib/store-features/with-linked-hypermedia-resource.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,50 @@ describe('withLinkedHypermediaResource', () => {
230230
httpTestingController.verify();
231231
});
232232

233+
it('resets state when link disappears from root resource after being loaded', async () => {
234+
const rootModelFromUrl: RootModel = {
235+
apiName: 'loaded model',
236+
_links: {
237+
testModel: { href: '/api/test-model' }
238+
}
239+
};
240+
241+
const testModelFromLink: TestModel = {
242+
name: 'from link'
243+
};
244+
245+
const loadRootModel = store.loadRootModelFromUrl('/api/root-model');
246+
247+
httpTestingController.expectOne('/api/root-model').flush(rootModelFromUrl);
248+
httpTestingController.verify();
249+
250+
await loadRootModel;
251+
await firstValueFrom(timer(0));
252+
253+
httpTestingController.expectOne('/api/test-model').flush(testModelFromLink);
254+
httpTestingController.verify();
255+
256+
await firstValueFrom(timer(0));
257+
258+
expect(store.testModelState.isLoaded()).toBeTrue();
259+
expect(store.testModelState.isAvailable()).toBeTrue();
260+
expect(store.testModel.name()).toBe('from link');
261+
262+
// Reload root model without the link
263+
const reloadRootModel = store.reloadRootModel();
264+
265+
httpTestingController.expectOne('/api/root-model').flush({ apiName: 'loaded model', _links: {} } satisfies RootModel);
266+
httpTestingController.verify();
267+
268+
await reloadRootModel;
269+
await firstValueFrom(timer(0));
270+
271+
expect(store.testModelState.isLoaded()).toBeFalse();
272+
expect(store.testModelState.isAvailable()).toBeFalse();
273+
expect(store.testModelState.isLoading()).toBeFalse();
274+
expect(store.testModel()).toBe(initialTestModel);
275+
276+
httpTestingController.verify();
277+
});
278+
233279
});

libs/ngrx-hateoas/src/lib/store-features/with-linked-hypermedia-resource.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@ export function withLinkedHypermediaResource<ResourceName extends string, TResou
153153
pipe(
154154
filter(resource => resource !== undefined),
155155
map(resource => hateoasService.getLink(resource, linkMetaName)?.href),
156+
tap(href => {
157+
if (!isValidHref(href)) {
158+
patchState(store,
159+
updateData(dataKey, initialValue),
160+
updateState(stateKey, { url: '', isLoading: false, isLoaded: false, isAvailable: false }));
161+
}
162+
}),
156163
filter(href => isValidHref(href)),
157164
map(href => href!),
158165
filter(href => getState(store, stateKey).url !== href),

0 commit comments

Comments
 (0)