Skip to content
This repository was archived by the owner on May 21, 2021. It is now read-only.

Commit 2c25c5c

Browse files
build(lib): Changed whole logic of Query library
1 parent 94f76b6 commit 2c25c5c

14 files changed

+370
-157
lines changed

README.md

+46-96
Original file line numberDiff line numberDiff line change
@@ -17,117 +17,67 @@ Library requires `@ngrx/store` module.
1717

1818
## Usage
1919

20-
State (for example `homepage.state.ts`):
20+
Import QueryModule in your app.module.ts:
2121
```ts
22-
import { queryReducer } from '@ngsm/query';
23-
import { HomepageApiResponseDto } from 'your-api-dto.interfaces.ts';
22+
import { QueryModule } from '@ngsm/query';
2423

25-
export interface HomepageQueryState {
26-
getHomepageApiQuery?: Query<HomepageApiResponseDto>;
27-
}
28-
29-
export const HOMEPAGE_QUERY_KEY = 'homepageQuery';
30-
31-
export interface HomepagePartialState {
32-
readonly [HOMEPAGE_QUERY_KEY]: HomepageQueryState;
33-
// Your feature states, for example:
34-
// readonly [HOMEPAGE_FEATURE_KEY]: HomepageState;
35-
}
36-
```
37-
38-
Reducer (for example `homepage.reducer.ts`):
39-
```ts
40-
import { Action } from '@ngrx/store';
41-
import { HomepageQueryState } from './homepage.state';
42-
43-
...
44-
45-
export function homepageQueryReducer(state: HomepageQueryState | undefined, action: Action) {
46-
return queryReducer(state, action);
24+
...,
25+
@NgModule({
26+
....,
27+
imports: [
28+
....,
29+
QueryModule
30+
]
4731
}
4832
```
4933
50-
Selectors (for example `homepage.selectors.ts`):
51-
```ts
52-
import { createFeatureSelector, createSelector } from '@ngrx/store';
53-
54-
export const homepageQueryState = createFeatureSelector<HomepagePartialState, HomepageQueryState>(HOMEPAGE_QUERY_KEY);
55-
56-
export const getHomepageApiQuery = createSelector(
57-
homepageQueryState,
58-
(state: HomepageQueryState) => state.getHomepageApiQuery
59-
);
60-
```
6134
62-
State module (for example `homepage-state.module.ts`):
35+
How to use @Query decorator in your HTTP service:
6336
```ts
64-
import { NgModule } from '@angular/core';
65-
import { EffectsModule } from '@ngrx/effects';
66-
import { StoreModule } from '@ngrx/store';
37+
import { Query } from '@ngsm/query';
38+
import { HttpClient } from '@angular/common/http';
6739

68-
import { HomapageEffects } from './homepage.effects';
69-
import { HomapageFacade } from './homepage.facade';
70-
import { homepageQueryReducer, homepageReducer } from './homepage.reducer';
71-
import { HOMEPAGE_QUERY_KEY } from './homepage.state';
72-
73-
@NgModule({
74-
imports: [
75-
StoreModule.forFeature(HOMEPAGE_QUERY_KEY, homepageQueryReducer),
76-
EffectsModule.forFeature([HomapageEffects]),
77-
],
78-
providers: [HomapageFacade]
79-
})
80-
export class HomapageStateModule {}
81-
```
40+
@Injectable()
41+
export class CarRepository {
42+
constructor(private http: HttpClient) { }
8243

83-
Effects (for example `homepage.effects.ts`):
84-
```ts
85-
...
86-
87-
getHomepageApi$ = createEffect(() =>
88-
this.actions$.pipe(
89-
ofType(HomepageActions.getHomepageApi),
90-
mergeMap(() => concat(
91-
// run inProgress action
92-
of(QueryActions.inProgress({ query: HomepageQuery.getHomepageApiQuery })),
93-
this.homepageRepository
94-
.getHomepageApi()
95-
.pipe(
96-
mergeMap((response) => [
97-
// run success action
98-
QueryActions.success({ query: HomepageQuery.getHomepageApiQuery, response }),
99-
]),
100-
catchError(error => [
101-
// run failure action
102-
QueryActions.failure({ query: HomepageQuery.getHomepageApiQuery, error }),
103-
])
104-
)
105-
))
106-
)
107-
);
108-
109-
...
44+
@Query({ name: 'getCars', groups: ['cars'] })
45+
getCars(): Observable<object> {
46+
return this.http.get<object>('API_URL');
47+
}
48+
}
11049
```
11150
112-
Facade (for example `homepage.facade.ts`):
51+
In your component (for example `car.component.ts`):
11352
```ts
11453
...
54+
import { QueryFacade } from '@ngsm/query';
55+
...
11556

116-
@Injectable()
117-
export class HomepageFacade {
118-
getHomepageQuery$ = this.store.pipe(select(HomepageSelectors.getHomepageQuery));
119-
120-
loader$ = isQueryInProgress$([
121-
this.getHomepageQuery$,
122-
// add all feature queries
123-
...,
124-
]);
125-
126-
constructor(private store: Store<HomepagePartialState>) {}
127-
128-
dispatch(action: Action) {
129-
this.store.dispatch(action);
57+
@Component({
58+
selector: 'app-cars',
59+
templateUrl: './cars.component.html',
60+
})
61+
export class CarsComponent implements OnInit, OnDestroy {
62+
// Available methods:
63+
loader$ = this.queryFacade.isInProgress$('cars');
64+
response$ = this.queryFacade.response$<YOUR_TYPE>('getCars');
65+
query$ = this.queryFacade.query$<YOUR_TYPE>('getCars');
66+
error$ = this.queryFacade.error$('getCars');
67+
status$ = this.queryFacade.status$('getCars');
68+
69+
constructor(
70+
private carRepository: CarRepository,
71+
private queryFacade: QueryFacade,
72+
) {}
73+
74+
ngOnInit() {
75+
this.carRepository
76+
.getCars()
77+
.subscribe();
13078
}
79+
80+
ngOnDestroy() { }
13181
}
13282
```
13383

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ngsm/query",
3-
"version": "0.1.0",
3+
"version": "0.2.1-alpha.0",
44
"private": false,
55
"dependencies": {
66
"@angular/common": "^9.1.0",

src/lib/index.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import * as QueryActions from './query.actions';
1+
import * as QueryActions from './store/query.actions';
22

33
export * from './query.model';
4-
export * from './query.reducers';
54
export * from './query.utils';
5+
export * from './query.decorator';
6+
export * from './query.helpers';
7+
export * from './store/query.facade';
8+
export * from './query.module';
69

710
export { QueryActions };

src/lib/query.decorator.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { map, catchError } from 'rxjs/operators';
2+
import { APP_BOOTSTRAP_LISTENER } from '@angular/core';
3+
import { Store } from '@ngrx/store';
4+
import { QueryConfig } from './query.model';
5+
import * as QueryActions from './store/query.actions';
6+
7+
let _store;
8+
9+
export const BOOTSTRAP_QUERY_PROVIDER = {
10+
provide: APP_BOOTSTRAP_LISTENER,
11+
multi: true,
12+
deps: [Store],
13+
useFactory: (s) => {
14+
_store = s;
15+
return (store) => store;
16+
}
17+
};
18+
19+
export const Query = (queryConfig: QueryConfig): MethodDecorator => (
20+
target: any,
21+
propertyKey: string,
22+
descriptor: PropertyDescriptor
23+
) => {
24+
const original = descriptor.value;
25+
26+
descriptor.value = function () {
27+
_store.dispatch(QueryActions.inProgress({ queryConfig }));
28+
29+
return original.apply(this, arguments)
30+
.pipe(
31+
map((response) => {
32+
_store.dispatch(QueryActions.success({ queryConfig, response }));
33+
return response;
34+
}),
35+
catchError((error) => {
36+
_store.dispatch(QueryActions.failure({ queryConfig, error }));
37+
throw error;
38+
})
39+
);
40+
};
41+
42+
return descriptor;
43+
};

src/lib/query.helpers.ts

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { QueryResponse, QueryConfig, QueryGroups, Queries } from './query.model';
2+
import { QueryState } from './store/query.state';
3+
import { isQueryGroupInProgress } from './query.utils';
4+
5+
export const getInitialQuery = (): QueryResponse<null> => ({
6+
status: null,
7+
response: null,
8+
error: null,
9+
isDirty: false,
10+
isInProgress: false,
11+
isSuccess: false,
12+
isError: false,
13+
});
14+
15+
export const getQueriesGroup = (
16+
queryState: QueryState,
17+
queryConfig: QueryConfig,
18+
query: QueryResponse<any>,
19+
groupName: string,
20+
): Queries[] => {
21+
const { groups } = queryState;
22+
const { name } = queryConfig;
23+
24+
return {
25+
...(groups[groupName] ? groups[groupName].queries : []),
26+
[name]: query,
27+
};
28+
};
29+
30+
export const getQueryGroups = (
31+
queryState: QueryState,
32+
queryConfig: QueryConfig,
33+
query: QueryResponse<any>
34+
): QueryGroups => {
35+
const { groups: stateGroups } = queryState;
36+
const { groups } = queryConfig;
37+
const extendedGroups = { ...stateGroups };
38+
39+
if (!groups) {
40+
return extendedGroups;
41+
}
42+
43+
groups.map(groupName => {
44+
const queries = getQueriesGroup(queryState, queryConfig, query, groupName);
45+
extendedGroups[groupName] = {
46+
isInProgress: isQueryGroupInProgress(queries),
47+
queries
48+
};
49+
});
50+
51+
return extendedGroups;
52+
};
53+
54+
export const getQueries = (
55+
queryState: QueryState,
56+
queryConfig: QueryConfig,
57+
query: QueryResponse<any>
58+
): Queries => {
59+
return {
60+
...queryState.queries,
61+
[queryConfig.name]: query
62+
};
63+
};
64+
65+
export const parseQueryState = (
66+
queryState: QueryState,
67+
queryConfig: QueryConfig,
68+
query: QueryResponse<any>
69+
): QueryState => {
70+
const groups = getQueryGroups(queryState, queryConfig, query);
71+
const queries = getQueries(queryState, queryConfig, query);
72+
73+
return {
74+
...queryState,
75+
queries,
76+
groups
77+
};
78+
};

src/lib/query.model.ts

+26-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
11
import { HttpErrorResponse } from '@angular/common/http';
22

3+
export interface Queries {
4+
[key: string]: QueryResponse<any>;
5+
}
6+
7+
export interface QueryGroup {
8+
queries: Queries[];
9+
isInProgress: boolean;
10+
}
11+
12+
export interface QueryGroups {
13+
[key: string]: QueryGroup;
14+
}
15+
16+
export interface QueryConfig {
17+
name: string;
18+
groups?: string[];
19+
}
20+
321
export enum QueryStatus {
422
Success = 'SUCCESS',
523
InProgress = 'IN_PROGRESS',
624
Failure = 'FAILURE',
725
}
826

9-
export interface Query<T> {
10-
status?: QueryStatus;
11-
response?: T;
12-
error?: HttpErrorResponse;
27+
export interface QueryResponse<T> {
28+
response: T;
29+
error: HttpErrorResponse;
30+
status: QueryStatus;
31+
isSuccess: boolean;
32+
isError: boolean;
33+
isInProgress: boolean;
34+
isDirty: boolean;
1335
}

src/lib/query.module.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { NgModule } from '@angular/core';
2+
import { StoreModule } from '@ngrx/store';
3+
import { QueryReducer } from './store/query.reducer';
4+
5+
import { QUERY_STORE_KEY } from './store/query.state';
6+
import { QueryFacade } from './store/query.facade';
7+
import { BOOTSTRAP_QUERY_PROVIDER } from './query.decorator';
8+
9+
@NgModule({
10+
imports: [
11+
StoreModule.forFeature(QUERY_STORE_KEY, QueryReducer),
12+
],
13+
providers: [
14+
QueryFacade,
15+
BOOTSTRAP_QUERY_PROVIDER
16+
]
17+
})
18+
export class QueryModule {}

0 commit comments

Comments
 (0)