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

Commit cf9a739

Browse files
feat(ngsm): query module
0 parents  commit cf9a739

19 files changed

+10661
-0
lines changed

.editorconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Editor configuration, see http://editorconfig.org
2+
root = true
3+
4+
[*]
5+
charset = utf-8
6+
indent_style = space
7+
indent_size = 2
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[*.md]
12+
max_line_length = off
13+
trim_trailing_whitespace = false

.gitignore

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# compiled output
2+
/dist
3+
/tmp
4+
/out-tsc
5+
6+
# dependencies
7+
/node_modules
8+
9+
# IDEs and editors
10+
/.idea
11+
.project
12+
.classpath
13+
.c9/
14+
*.launch
15+
.settings/
16+
*.sublime-workspace
17+
18+
# IDE - VSCode
19+
.vscode/*
20+
!.vscode/settings.json
21+
!.vscode/tasks.json
22+
!.vscode/launch.json
23+
!.vscode/extensions.json
24+
25+
# misc
26+
/.sass-cache
27+
/connect.lock
28+
/coverage
29+
/libpeerconnection.log
30+
npm-debug.log
31+
yarn-error.log
32+
testem.log
33+
/typings
34+
35+
# System Files
36+
.DS_Store
37+
Thumbs.db

README.md

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<p align="center">
2+
<a href="https://github.com/ng-sm">
3+
<img src="https://avatars2.githubusercontent.com/u/64587411?s=150">
4+
</a>
5+
</p>
6+
<p align="center">
7+
Query module based on the @ngrx/store package.
8+
</p>
9+
10+
## Installation
11+
12+
`yarn add @ngsm/query` or `npm i @ngsm/query --save`
13+
14+
## Requirements
15+
16+
Library requires `@ngrx/store` module.
17+
18+
## Usage
19+
20+
State (for example `homepage.state.ts`):
21+
```ts
22+
import { queryReducer } from '@ngsm/query';
23+
import { HomepageApiResponseDto } from 'your-api-dto.interfaces.ts';
24+
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);
47+
}
48+
```
49+
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+
```
61+
62+
State module (for example `homepage-state.module.ts`):
63+
```ts
64+
import { NgModule } from '@angular/core';
65+
import { EffectsModule } from '@ngrx/effects';
66+
import { StoreModule } from '@ngrx/store';
67+
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+
```
82+
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+
...
110+
```
111+
112+
Facade (for example `homepage.facade.ts`):
113+
```ts
114+
...
115+
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);
130+
}
131+
}
132+
```
133+
134+
## Author
135+
136+
<table border="0">
137+
<tr>
138+
<td>
139+
<a href="https://github.com/sebastianmusial" style="color: white">
140+
<img src="https://github.com/sebastianmusial.png?s=150" width="150"/>
141+
</a>
142+
</td>
143+
<td>
144+
<p><strong>Sebastian Musiał</strong></p>
145+
<p><img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDQ4IDQ4IiBoZWlnaHQ9IjQ4cHgiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDQ4IDQ4IiB3aWR0aD0iNDhweCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGcgaWQ9IkV4cGFuZGVkIj48Zz48Zz48cGF0aCBkPSJNNDQsNDBINGMtMi4yMDYsMC00LTEuNzk0LTQtNFYxMmMwLTIuMjA2LDEuNzk0LTQsNC00aDQwYzIuMjA2LDAsNCwxLjc5NCw0LDR2MjRDNDgsMzguMjA2LDQ2LjIwNiw0MCw0NCw0MHogTTQsMTAgICAgIGMtMS4xMDMsMC0yLDAuODk3LTIsMnYyNGMwLDEuMTAzLDAuODk3LDIsMiwyaDQwYzEuMTAzLDAsMi0wLjg5NywyLTJWMTJjMC0xLjEwMy0wLjg5Ny0yLTItMkg0eiIvPjwvZz48Zz48cGF0aCBkPSJNMjQsMjkuMTkxTDYuNDU3LDE3Ljg0Yy0wLjQ2NC0wLjMwMS0wLjU5Ny0wLjkxOS0wLjI5Ny0xLjM4M3MwLjkxOS0wLjU5NiwxLjM4My0wLjI5N0wyNCwyNi44MDlMNDAuNDU3LDE2LjE2ICAgICBjMC40NjQtMC4yOTksMS4wODMtMC4xNjcsMS4zODMsMC4yOTdzMC4xNjcsMS4wODItMC4yOTcsMS4zODNMMjQsMjkuMTkxeiIvPjwvZz48Zz48cGF0aCBkPSJNNi4wMDEsMzRjLTAuMzIzLDAtMC42NDEtMC4xNTYtMC44MzMtMC40NDVjLTAuMzA3LTAuNDYtMC4xODMtMS4wOCwwLjI3Ny0xLjM4N2w5LTZjMC40Ni0wLjMwNywxLjA4MS0wLjE4MywxLjM4NywwLjI3NyAgICAgYzAuMzA3LDAuNDYsMC4xODMsMS4wOC0wLjI3NywxLjM4N2wtOSw2QzYuMzg0LDMzLjk0NSw2LjE5MSwzNCw2LjAwMSwzNHoiLz48L2c+PGc+PHBhdGggZD0iTTQxLjk5OSwzNGMtMC4xOSwwLTAuMzgzLTAuMDU1LTAuNTU0LTAuMTY4bC05LTZjLTAuNDYtMC4zMDctMC41ODQtMC45MjctMC4yNzctMS4zODcgICAgIGMwLjMwNi0wLjQ2LDAuOTI2LTAuNTg0LDEuMzg3LTAuMjc3bDksNmMwLjQ2LDAuMzA3LDAuNTg0LDAuOTI3LDAuMjc3LDEuMzg3QzQyLjY0LDMzLjg0NCw0Mi4zMjIsMzQsNDEuOTk5LDM0eiIvPjwvZz48L2c+PC9nPjwvc3ZnPg==" style="width: 18px; height: 18px; vertical-align: middle; margin: 0 5px 5px 0"> <a href="mailto:kontakt@sebastianmusial.pl">kontakt@sebastianmusial.pl</a></p>
146+
<p><img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGhlaWdodD0iNTEycHgiIGlkPSLlvaLnirZfMl8xXyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNTEyIDUxMjsiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHdpZHRoPSI1MTJweCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGcgaWQ9IuW9oueKtl8yIj48Zz48cGF0aCBkPSJNNDg1Ljk4LDExMy4xNDFjLTE2LjkyMyw3LjUwNi0zNS4xMDksMTIuNTc4LTU0LjE5NywxNC44NTggICAgYzE5LjQ4LTExLjY3OSwzNC40NDUtMzAuMTcxLDQxLjQ5LTUyLjIwOGMtMTguMjM0LDEwLjgxNC0zOC40MywxOC42NjgtNTkuOTI1LDIyLjg5OWMtMTcuMjEzLTE4LjM0MS00MS43MzgtMjkuNzk5LTY4Ljg4LTI5Ljc5OSAgICBjLTUyLjExNCwwLTk0LjM2OCw0Mi4yNS05NC4zNjgsOTQuMzY0YzAsNy4zOTYsMC44MzQsMTQuNTk4LDIuNDQ0LDIxLjUwNWMtNzguNDI3LTMuOTM2LTE0Ny45NjItNDEuNTA0LTE5NC41MDQtOTguNTk3ICAgIGMtOC4xMjMsMTMuOTM3LTEyLjc3NywzMC4xNDYtMTIuNzc3LDQ3LjQ0MWMwLDMyLjczOSwxNi42NTksNjEuNjIzLDQxLjk4LDc4LjU0NmMtMTUuNDY5LTAuNDkxLTMwLjAyLTQuNzM1LTQyLjc0Mi0xMS44MDQgICAgYy0wLjAwOSwwLjM5NS0wLjAwOSwwLjc4OC0wLjAwOSwxLjE4OGMwLDQ1LjcyMSwzMi41MjksODMuODU5LDc1LjY5OCw5Mi41MzFjLTcuOTE4LDIuMTU2LTE2LjI1NSwzLjMxMS0yNC44NjEsMy4zMTEgICAgYy02LjA4MSwwLTExLjk5Mi0wLjU5My0xNy43NTUtMS42OTNjMTIuMDA5LDM3LjQ4OCw0Ni44NTgsNjQuNzczLDg4LjE1Myw2NS41MzNjLTMyLjI5NiwyNS4zMTItNzIuOTg1LDQwLjM5Ni0xMTcuMTk4LDQwLjM5NiAgICBjLTcuNjE3LDAtMTUuMTI4LTAuNDQ2LTIyLjUxMS0xLjMyYzQxLjc2MiwyNi43NzUsOTEuMzY1LDQyLjQsMTQ0LjY1NSw0Mi40YzE3My41NzQsMCwyNjguNDkzLTE0My43OTQsMjY4LjQ5My0yNjguNDk2ICAgIGMwLTQuMDkxLTAuMDkyLTguMTYtMC4yNzMtMTIuMjA4QzQ1Ny4zMzIsMTQ4LjY4NCw0NzMuMzMsMTMyLjA2NCw0ODUuOTgsMTEzLjE0MXoiIHN0eWxlPSJmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiMyQ0E3RTA7Ii8+PC9nPjwvZz48L3N2Zz4=" style="width: 18px; height: 18px; vertical-align: middle; margin: 0 5px 5px 0"> <a href="https://twitter.com/SebaMusial">@sebamusial</a></p>
147+
</td>
148+
</tr>
149+
</table>

angular.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"version": 1,
3+
"projects": {
4+
"query": {
5+
"projectType": "library",
6+
"root": "",
7+
"sourceRoot": "./src",
8+
"prefix": "ngsm",
9+
"architect": {
10+
"build": {
11+
"builder": "@angular-devkit/build-ng-packagr:build",
12+
"options": {
13+
"tsConfig": "./tsconfig.lib.json",
14+
"project": "./ng-package.json"
15+
}
16+
},
17+
"lint": {
18+
"builder": "@angular-devkit/build-angular:tslint",
19+
"options": {
20+
"tsConfig": [
21+
"./tsconfig.lib.json",
22+
"./tsconfig.spec.json"
23+
],
24+
"exclude": ["**/node_modules/**", "!./**"]
25+
}
26+
},
27+
"test": {
28+
"builder": "@nrwl/jest:jest",
29+
"options": {
30+
"jestConfig": "./jest.config.js",
31+
"tsConfig": "./tsconfig.spec.json",
32+
"setupFile": "./src/test-setup.ts",
33+
"passWithNoTests": true,
34+
"codeCoverage": true
35+
}
36+
}
37+
}
38+
}
39+
},
40+
"defaultProject": "query"
41+
}

jest.config.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module.exports = {
2+
name: 'query',
3+
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
4+
transform: {
5+
'^.+\\.(ts|js|html)$': 'ts-jest'
6+
},
7+
resolver: '@nrwl/jest/plugins/resolver',
8+
moduleFileExtensions: ['ts', 'js', 'html'],
9+
coverageReporters: ['html'],
10+
coverageDirectory: './coverage/query',
11+
snapshotSerializers: [
12+
'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js',
13+
'jest-preset-angular/build/AngularSnapshotSerializer.js',
14+
'jest-preset-angular/build/HTMLCommentSerializer.js'
15+
]
16+
};

ng-package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
3+
"dest": "./dist/query",
4+
"lib": {
5+
"entryFile": "src/index.ts"
6+
},
7+
"whitelistedNonPeerDependencies": ["."]
8+
}

package.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "@ngsm/query",
3+
"version": "0.1.0",
4+
"private": false,
5+
"dependencies": {
6+
"@angular/common": "^9.1.0",
7+
"@angular/core": "~9.1.0",
8+
"@angular/compiler": "^9.1.4",
9+
"@ngrx/store": "9.0.0",
10+
"rxjs": "~6.5.4",
11+
"zone.js": "~0.10.2"
12+
},
13+
"devDependencies": {
14+
"@angular-devkit/build-angular": "~0.901.0",
15+
"@angular-devkit/build-ng-packagr": "~0.901.0",
16+
"@angular/cli": "~9.1.0",
17+
"@angular/compiler-cli": "~9.1.0",
18+
"@types/jest": "25.1.4",
19+
"@nrwl/jest": "9.2.3",
20+
"ng-packagr": "^9.0.0",
21+
"tsickle": "^0.38.1",
22+
"jest": "25.2.4",
23+
"jest-preset-angular": "8.1.3"
24+
},
25+
"scripts": {
26+
"build": "ng build",
27+
"test": "ng test",
28+
"bump": "npm --no-git-tag-version version patch",
29+
"prepublish": "yarn build && cd dist/query",
30+
"release": "yarn prepublish && npm publish --access public"
31+
},
32+
"keywords": [
33+
"Angular",
34+
"JavaScript",
35+
"RxJs",
36+
"store",
37+
"ngrx",
38+
"ngrx-store",
39+
"ngrx-query",
40+
"query"
41+
]
42+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib/index';

src/lib/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import * as QueryActions from './query.actions';
2+
3+
export * from './query.model';
4+
export * from './query.reducers';
5+
export * from './query.utils';
6+
7+
export { QueryActions };

src/lib/query.actions.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { HttpErrorResponse } from '@angular/common/http';
2+
import { createAction, props } from '@ngrx/store';
3+
4+
export const QUERY_KEY = '[Query]';
5+
6+
export const init = createAction(
7+
`${QUERY_KEY} INIT`,
8+
props<{ query: string }>(),
9+
);
10+
11+
export const inProgress = createAction(
12+
`${QUERY_KEY} IN_PROGRESS`,
13+
props<{ query: string }>(),
14+
);
15+
16+
export const success = createAction(
17+
`${QUERY_KEY} SUCCESS`,
18+
props<{ query: string, response?: any }>(),
19+
);
20+
21+
export const failure = createAction(
22+
`${QUERY_KEY} FAILURE`,
23+
props<{ query: string, error: HttpErrorResponse }>(),
24+
);
25+
26+
export const clear = createAction(
27+
`${QUERY_KEY} CLEAR`,
28+
props<{ query: string }>(),
29+
);

0 commit comments

Comments
 (0)