Reputation: 1635
To reduce network traffic I need to check if the entity with the given id is in the store and if not, it should be requested from the server. I couldn't find any configuration for that, so I just hard coded the logic using RxJs like below.
Is there a way to configure that with NgRx/NgRx data? Is there a way to configure how long can an entity be in the store without need to rerequest?
import {
EntityCollectionServiceBase,
EntityCollectionServiceElementsFactory,
} from '@ngrx/data';
import { Injectable } from '@angular/core';
import { MyEntity } from 'app/state/my-entity/MyEntity';
import { Observable, of, Subject } from 'rxjs';
import { distinct, filter, switchMap, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class MyEntityDataService extends EntityCollectionServiceBase<MyEntity> {
private id$ = new Subject<string|number>();
constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
super('MyEntity', serviceElementsFactory);
// Fetching the data if not yet there
this.id$.pipe(
// Preventing multiple requests e.g. when multiple components
// are requesting the same data
distinct(),
switchMap(
(id: string|number): Observable<MyEntity | undefined> => this.filteredEntities$.pipe(
switchMap(
(entityCollection: MyEntity[]): Observable<MyEntity | undefined> => {
const thatEntity: MyEntity | undefined =
entityCollection.find((entity: MyEntity) => `${entity.id}` === `${id}`);
return of(thatEntity).pipe(
filter((v: MyEntity | undefined): boolean => !v),
tap(() => super.getByKey(id))
)
}
),
),
),
).subscribe();
}
getByKey(id: string|number): Observable<MyEntity> {
this.id$.next(id);
return this.filteredEntities$.pipe(
switchMap(
(entityCollection: MyEntity[]) : Observable<MyEntity> => {
const thatEntity: MyEntity | undefined = entityCollection.find(
(entity: MyEntity) => `${entity.id}` === `${id}`
);
return thatEntity ? of(thatEntity) : of();
}
),
);
}
}
Upvotes: 1
Views: 437
Reputation: 567
I faced the same problem and the best solution for me was to check it on the effect before dispatch the action.
//selectIdExists:selector recieve ID and return true if loaded or false if not found
//user should dispatch initialFindById and the effect will decide either to dispatch findById or no dispatching
initialFindById$ = createEffect(() => this.actions$.pipe(
ofType(actions.initialFindById),
mergeMap(action => this.store.select(selectors.selectIdExists(action.id))
.pipe(
filter(found => found == false),//Id not found
map(() => actions.findById({ id: action.id })),
))
));
if you want to make sure to fetch data even if there is data on the store simply dispatch findbyId
action instead
Upvotes: 1