It is built with rxjs and designed easy to use. queryStore is Singleton, only one exists throughout the app.
This is a work in progress, please do not install it in your project yet!!!!
This project was generated with Angular CLI version 13.2.2.
Sample Test Url: https://stackblitz.com/edit/angular-ivy-vxsmmj?file=src/app/app.component.ts
name | type | description |
---|---|---|
key | string | required should be unique for query and store |
initState | any | required initial state for view data |
query | function (args: any): Observable<any> | defaultValue: (s) => of(s) async operations, for static store you can use it for transformer |
isEqual | function (a:any, b:any):boolean | defaultValue: shallowEqual(with 2 deps) fn for distinctUntilChange for cache |
retry | number | defaultValue: 2 with an error, how many times more tries the query. |
retryDelay | number | defaultValue: 3 (3seconds) delay between retries |
staticStore | boolean | defaultValue: false ignore all refetch strategy |
name | type | description |
---|---|---|
prefetch | {param: any} | defaultValue: null perform fetch with registration, the data prop goes to fetch argument |
refetchOnReconnect | boolean | defaultValue: false perform fetch with registration, the data prop goes to fetch argument. |
refetchOnEmerge | boolean | defaultValue: false refetch on window.visibilityChange => document.visibilityState === 'visible'. |
refetchInterval | number | defaultValue: 24 * 3600 (1 day) min value is 2(2seconds) interval calls. restart if query called and successed. unit is second |
staleModeDuration | number | defaultValue: 300 (5 minutes) duration to perform event of merging refetchOnReconnect & refetchOnEmerge and only if staleModeDuration has passed from last fetch, refetch works. unit is second(5 === 5second) |
refetchOnStaleMode | boolean | defaultValue: false by default, refetch action by refetchInterval does not work when it is on the stale mode, with true, it ignores default and perform refetch |
keepAlive | boolean | defaultValue: false After destroying store, keep the cache |
caching | number | defaultValue: 0(cache only one for initial hash) min: 0, max: 50 number of caching for previous response. max is 30 |
paramToHash | function: (p: param) => string | defaultValue: undefined util to get cash hash key from query param, by the hashkey count of cache object varies |
you can add rxQueryCachingKey key to param for query and it has more priority to get hash. |
inside store has 2 classes one for RxQuery, one for RxStore RxQuery is to use refetch & cache strategy RxStore is to use storage for cache and you can still transform by query option
import { RxNgQueryModule } from 'rx-ng-query';
interface RootStoreState {...}
const storeInitiator: (...arg: any[]) => RxQueryOption[] = (apiService: ApiService ) => [
{ key: 'limit', initState: { daily: 0, monthly: 0 }, query: apiService.fetchLimit.bind(apiService) },
{ key: 'goods', initState: [], query: apiService.fetchGoods.bind(apiService) },
];
const storeFetchDependency = [ApiService]; // this will be injected as storeInit arguments
imports: [
ApiService, // custom service
RxNgQueryModule.withInitStore<RootStoreState>(
storeInitiator,
storeFetchDependency
),
]
// or if you don't have initial store just import RxNgQueryModule
imports: [
RxNgQueryModule
]
// in component
constructor(private rxNgQueryStore: RxNgQueryStore < any >,
private apiService: ApiService,
)
{
// rxQueryStore.registerStore(options);
rxNgQueryStore.registerStore({
key: 'limit',
initState: [],
prefetch: {param: 'daily'},
query: apiService.fetchLimit.bind(apiService)
});
}
// as service
@Injectable()
@RxQueryService()
export class HistoryApiService {
constructor(private apiService: ApiService) {
}
// prefetch for fetching with registration
@RxQuery({key: 'history', initState: [], prefetch: {param: null}})
fetchHistory() {
// this function goes to query and calling it calling fetch.
return this.apiService.fetchHistory();
}
}
// Don't forget to receive injection of service at the component,
// otherwise they does not work.
@Component({
...,
providers: [HistoryApiService],
})
export class SomeComponent {
constructor(private historyApiService: HistoryApiService) {
// you should inject inside the component
// otherwise it will not initiated.
}
}
RxNgQueryStore is the manager and the bridge to each store. it provides methods to each store we declared.
method | supports | description |
---|---|---|
registerStore(options: RxQueryOption):void | both | create store |
unregisterStore(key: string):void | both | destroy store, if keepAlive is true, the state can be cached |
has(key: string):boolean | both | check for store existance |
getInitData(key: string):any | both | get the initdata of the store you inject on registration |
reset(key: string):void | both | reset the store, remove cache & all the flag to the start state |
select(key: string, selector?:(s: any) => any):Observable<RxQueryStatus['data']> | both | select from status.data, selector is mapping function |
status(key: string):Observable<RxQueryStatus> | both | select from status.data, selector is mapping function |
mutate(key: string, fn:<RxQueryStatus['data']>) => <RxQueryStatus['data']>):boolean | both | use to manually mutate the data, if the query is executing, it can be denied and the result of success returned |
fetch(key: string, param: any) => void | both | fetch with new param |
refetch(key: string) => void | RxQuery only | refetch with latest param, it can reset the refetchInterval |
disableRefetch(key: string, disabled: boolean) => void | RxQuery only | pause refetch or not |
status(key) 로 해당 스트림을 얻을 수 있다.
- data: returned data from query
- ts: timestamp that updated (in case of error, it does not update the ts)
- error: thrown error from query (it is reset on loading status)
- loading: loading status
- untrustedData: if data is same as initdata or error on fetch (in case of refetch, it keeps the current one)
// in template
<div>
<ng-template
[rxNgSuspense]="rxNgQueryStore.status('key') | async"
[loadingTemplate]="loadingTemplate"
[errorTemplate]="errorTemplate"
let-data
>
<div>{{ data.name }}</div>
</ng-template>
<ng-template #loadingTemplate>isLoading...</ng-template>
<ng-template #errorTemplate>isError...</ng-template>
</div>
ng-template with rxNgSuspense takes store's status and renders on the data. if content is ready the data you stored can be received template variable let-variableName
TemplateType: 'null' | 'loading' | 'content' | 'error'
rxNgSuspense also takes templateSort function((s: RxQueryStatus): TemplateType)
defaultTemplateSort(status: RxQueryStatus<any>){
const { loading, data, error, untrustedData } = status;
if (untrustedData) {
if (error) {
return 'error';
}
if (loading) {
return 'loading';
}
return data ? 'content' : 'null';
}
if (data) {
return 'content';
}
return 'null';
}