Skip to content

Commit

Permalink
feat: add selectors for tracking if loads were attempted
Browse files Browse the repository at this point in the history
 + Add selectHasBeenLoaded to track if the data has ever been loaded before
 + Add selectLoadWasAttempted to track if an attempt to load the data has ever been made
 ^ Bumped version to 0.8.1
 * Updated changelog to reflect latest changes

Issue #218
  • Loading branch information
Jon Rista committed May 5, 2022
1 parent 919dd94 commit 93ba371
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 2 deletions.
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
<a name="0.8.1"></a>

# [0.8.1](https://github.com/briebug/ngrx-auto-entity/compare/0.8.1-beta.1...0.8.1) Beta (2022-05-05)

Added two new loading related selectors: hasBeenLoaded and loadWasAttempted. These selectors allow
end developers to determine if a load has ever been attempted before, which is sometimes necessary
to display the correct information in a UI component. Until a load has at least been attempted, it
would generally be inappropriate to display to the user that there are no Entities X, however as
the current state of each entity currently stands, there is no way to determine that particular
state of an entity. You can determine if an entity is loading or not, which is useful for displaying
a spinner, but other messaging requires more information.

### Features

- **selectors:** Added selectHasBeenLoaded to selector map
- **selectors:** Added selectLoadWasAttempted to selector map


<a name="0.8.0-beta.1"></a>

# [0.7.2](https://github.com/briebug/ngrx-auto-entity/compare/0.7.2...0.8.1-beta.1) Beta (2022-04-05)
# [0.8.0-beta.1](https://github.com/briebug/ngrx-auto-entity/compare/0.7.2...0.8.1-beta.1) Beta (2022-04-05)

Updated state builders to build all state functionality "on-demand" to limit memory footprint when
lots of entities are used. This aligns selectors, facades, etc. with the way actions were implemented
Expand Down
2 changes: 1 addition & 1 deletion projects/ngrx-auto-entity/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.8.0-beta.1",
"version": "0.8.1",
"name": "@briebug/ngrx-auto-entity",
"description": "Automatic Entity State and Facades for NgRx. Simplifying reactive state!",
"license": "SEE LICENSE IN LICENSE.md",
Expand Down
10 changes: 10 additions & 0 deletions projects/ngrx-auto-entity/src/lib/selectors/tracking.selectors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { IEntityTracking } from '../util/entity-state';

// prettier-ignore
export const mapToHasBeenLoaded =
(tracking: IEntityTracking): boolean =>
tracking?.loadedAt != null;

// prettier-ignore
export const mapToLoadWasAttempted =
(tracking: IEntityTracking): boolean =>
tracking?.isLoading != null;

// prettier-ignore
export const mapToIsLoading =
(tracking: IEntityTracking): boolean =>
Expand Down
4 changes: 4 additions & 0 deletions projects/ngrx-auto-entity/src/lib/util/facade-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export const buildFacade = <TModel, TParentState>(selectors: ISelectorMap<TParen
this.currentPage$ = this.store.select(selectors.selectCurrentPage);
this.currentRange$ = this.store.select(selectors.selectCurrentRange);
this.totalPageable$ = this.store.select(selectors.selectTotalPageable);
this.hasBeenLoaded$ = this.store.select(selectors.selectHasBeenLoaded);
this.loadWasAttempted$ = this.store.select(selectors.selectLoadWasAttempted);
this.isLoading$ = this.store.select(selectors.selectIsLoading);
this.isSaving$ = this.store.select(selectors.selectIsSaving);
this.isDeleting$ = this.store.select(selectors.selectIsDeleting);
Expand Down Expand Up @@ -80,6 +82,8 @@ export const buildFacade = <TModel, TParentState>(selectors: ISelectorMap<TParen
currentPage$: Observable<Page>;
currentRange$: Observable<Range>;
totalPageable$: Observable<number>;
hasBeenLoaded$: Observable<boolean>;
loadWasAttempted$: Observable<boolean>;
isLoading$: Observable<boolean>;
isSaving$: Observable<boolean>;
isDeleting$: Observable<boolean>;
Expand Down
2 changes: 2 additions & 0 deletions projects/ngrx-auto-entity/src/lib/util/facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export interface IEntityFacade<TModel> {
currentPage$: Observable<Page>;
currentRange$: Observable<Range>;
totalPageable$: Observable<number>;
hasBeenLoaded$: Observable<boolean>;
loadWasAttempted$: Observable<boolean>;
isLoading$: Observable<boolean>;
isSaving$: Observable<boolean>;
isDeleting$: Observable<boolean>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const selectorProperties = [
'selectCurrentPage',
'selectCurrentRange',
'selectTotalPageable',
'selectHasBeenLoaded',
'selectLoadWasAttempted',
'selectIsLoading',
'selectIsSaving',
'selectIsDeleting',
Expand Down Expand Up @@ -424,4 +426,76 @@ describe('buildSelectorMap()', () => {
expect(entities).toBeObservable(hot('a', { a: false }));
});
});

describe('selectHasBeenLoaded', () => {
it('should return true if the loadedAt date is non-nullish', () => {
const store: MockStore<{}> = TestBed.inject(MockStore);

store.resetSelectors();
store.setState({
test: {
tracking: {
loadedAt: Date.now()
}
}
});

const getState = state => state.test;

const { selectHasBeenLoaded } = buildSelectorMap<ITestState, IEntityState<Test>, Test, unknown>(getState);
const hasBeen = store.select(selectHasBeenLoaded);
expect(hasBeen).toBeObservable(hot('a', { a: true }));
});

it('should return false if the loadedAt date is nullish', () => {
const store: MockStore<{}> = TestBed.inject(MockStore);

store.resetSelectors();
store.setState({
test: {}
});

const getState = state => state.test;

const { selectHasBeenLoaded } = buildSelectorMap<ITestState, IEntityState<Test>, Test, unknown>(getState);
const hasBeen = store.select(selectHasBeenLoaded);
expect(hasBeen).toBeObservable(hot('a', { a: false }));
});
});

describe('selectLoadWasAttempted', () => {
it('should return true if the isLoading tracking flag is non-nullish', () => {
const store: MockStore<{}> = TestBed.inject(MockStore);

store.resetSelectors();
store.setState({
test: {
tracking: {
isLoading: false
}
}
});

const getState = state => state.test;

const { selectLoadWasAttempted } = buildSelectorMap<ITestState, IEntityState<Test>, Test, unknown>(getState);
const hasBeen = store.select(selectLoadWasAttempted);
expect(hasBeen).toBeObservable(hot('a', { a: true }));
});

it('should return false if the isLoading tracking flag is nullish', () => {
const store: MockStore<{}> = TestBed.inject(MockStore);

store.resetSelectors();
store.setState({
test: {}
});

const getState = state => state.test;

const { selectLoadWasAttempted } = buildSelectorMap<ITestState, IEntityState<Test>, Test, unknown>(getState);
const hasBeen = store.select(selectLoadWasAttempted);
expect(hasBeen).toBeObservable(hot('a', { a: false }));
});
});
});
10 changes: 10 additions & 0 deletions projects/ngrx-auto-entity/src/lib/util/selector-map-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ import {
import {
mapToCreatedAt,
mapToDeletedAt,
mapToHasBeenLoaded,
mapToIsDeleting,
mapToIsLoading,
mapToIsSaving,
mapToLoadedAt,
mapToLoadWasAttempted,
mapToReplacedAt,
mapToSavedAt,
mapToUpdatedAt
Expand Down Expand Up @@ -133,6 +135,14 @@ export const buildSelectorMap = <TParentState, TState extends IEntityState<TMode
}

// Tracking:
get selectHasBeenLoaded() {
return createSelector(this.selectTracking, mapToHasBeenLoaded);
}

get selectLoadWasAttempted() {
return createSelector(this.selectTracking, mapToLoadWasAttempted);
}

get selectIsLoading() {
return createSelector(this.selectTracking, mapToIsLoading);
}
Expand Down
2 changes: 2 additions & 0 deletions projects/ngrx-auto-entity/src/lib/util/selector-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export interface ISelectorMap<TParentState, TModel> {
selectCurrentPage: MemoizedSelector<object | TParentState, Page | undefined>;
selectCurrentRange: MemoizedSelector<object | TParentState, Range | undefined>;
selectTotalPageable: MemoizedSelector<object | TParentState, number>;
selectHasBeenLoaded: MemoizedSelector<object | TParentState, boolean>;
selectLoadWasAttempted: MemoizedSelector<object | TParentState, boolean>;
selectIsLoading: MemoizedSelector<object | TParentState, boolean>;
selectIsSaving: MemoizedSelector<object | TParentState, boolean>;
selectIsDeleting: MemoizedSelector<object | TParentState, boolean>;
Expand Down
8 changes: 8 additions & 0 deletions projects/ngrx-auto-entity/src/lib/util/state-builder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ describe('buildState()', () => {
expect(built.selectors).toSatisfy(selectors => selectors.__proto__.hasOwnProperty('selectCurrentPage'));
expect(built.selectors).toSatisfy(selectors => selectors.__proto__.hasOwnProperty('selectCurrentRange'));
expect(built.selectors).toSatisfy(selectors => selectors.__proto__.hasOwnProperty('selectTotalPageable'));
expect(built.selectors).toSatisfy(selectors => selectors.__proto__.hasOwnProperty('selectHasBeenLoaded'));
expect(built.selectors).toSatisfy(selectors => selectors.__proto__.hasOwnProperty('selectLoadWasAttempted'));
expect(built.selectors).toSatisfy(selectors => selectors.__proto__.hasOwnProperty('selectIsLoading'));
expect(built.selectors).toSatisfy(selectors => selectors.__proto__.hasOwnProperty('selectIsSaving'));
expect(built.selectors).toSatisfy(selectors => selectors.__proto__.hasOwnProperty('selectIsDeleting'));
Expand Down Expand Up @@ -142,6 +144,8 @@ describe('buildState()', () => {
selectHasNoEntities: hasNoTestEntities,
selectIsDeleting: testIsDeleting,
selectIsDirty: testIsDirty,
selectHasBeenLoaded: testHasBeenLoaded,
selectLoadWasAttempted: testLoadWasAttempted,
selectIsLoading: testIsLoading,
selectIsSaving: testIsSaving,
selectLoadedAt: testLoadedAt,
Expand Down Expand Up @@ -176,6 +180,8 @@ describe('buildState()', () => {
selectHasNoEntities: hasNoTest2Entities,
selectIsDeleting: test2IsDeleting,
selectIsDirty: test2IsDirty,
selectHasBeenLoaded: test2HasBeenLoaded,
selectLoadWasAttempted: test2LoadWasAttempted,
selectIsLoading: test2IsLoading,
selectIsSaving: test2IsSaving,
selectLoadedAt: test2LoadedAt,
Expand Down Expand Up @@ -214,6 +220,8 @@ describe('buildState()', () => {
expect(hasNoTestEntities).not.toEqual(hasNoTest2Entities);
expect(testIsDeleting).not.toEqual(test2IsDeleting);
expect(testIsDirty).not.toEqual(test2IsDirty);
expect(testHasBeenLoaded).not.toEqual(test2HasBeenLoaded);
expect(testLoadWasAttempted).not.toEqual(test2LoadWasAttempted);
expect(testIsLoading).not.toEqual(test2IsLoading);
expect(testIsSaving).not.toEqual(test2IsSaving);
expect(testLoadedAt).not.toEqual(test2LoadedAt);
Expand Down
2 changes: 2 additions & 0 deletions projects/ngrx-auto-entity/src/lib/util/util.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const selectorProperties = [
'selectCurrentPage',
'selectCurrentRange',
'selectTotalPageable',
'selectHasBeenLoaded',
'selectLoadWasAttempted',
'selectIsLoading',
'selectIsSaving',
'selectIsDeleting',
Expand Down

0 comments on commit 93ba371

Please sign in to comment.