From 3613d4a5ea19a3ca0a0f819cd74814715dd286c0 Mon Sep 17 00:00:00 2001 From: tetra Date: Sat, 18 Sep 2021 19:53:31 +0430 Subject: [PATCH 1/4] style: "add eslint-plugin-ngrx as dev-dependencies" fix: "Remove some pipe from selector: selectors should be used with select method" --- .eslintrc | 7 ++++++- package.json | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 8324f13fa..474e65e60 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,13 +4,18 @@ "es6": true, "node": true }, - "extends": ["prettier", "prettier/@typescript-eslint"], + "extends": [ + "prettier", + "prettier/@typescript-eslint", + "plugin:ngrx/recommended" + ], "parser": "@typescript-eslint/parser", "parserOptions": { "project": "tsconfig.json", "sourceType": "module" }, "plugins": [ + "ngrx", "eslint-plugin-import", "@angular-eslint/eslint-plugin", "@typescript-eslint", diff --git a/package.json b/package.json index a77483f50..f97a18eee 100755 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "eslint": "^7.13.0", "eslint-config-prettier": "^6.15.0", "eslint-plugin-import": "^2.22.1", + "eslint-plugin-ngrx": "1.39.0", "express": "^4.16.4", "husky": "^4.3.0", "jasmine-core": "~3.6.0", From 93f26ad1d4cf85dfc5694acfe0dac5919842343e Mon Sep 17 00:00:00 2001 From: tetra Date: Sat, 18 Sep 2021 21:51:07 +0430 Subject: [PATCH 2/4] fix: "Remove some pipe from selector: selectors should be used with select method" --- .../src/app/app/app.component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/angular-ngrx-material-starter/src/app/app/app.component.ts b/projects/angular-ngrx-material-starter/src/app/app/app.component.ts index a7a1547a2..120bea850 100755 --- a/projects/angular-ngrx-material-starter/src/app/app/app.component.ts +++ b/projects/angular-ngrx-material-starter/src/app/app/app.component.ts @@ -69,9 +69,9 @@ export class AppComponent implements OnInit { ); } - this.isAuthenticated$ = this.store.pipe(select(selectIsAuthenticated)); - this.stickyHeader$ = this.store.pipe(select(selectSettingsStickyHeader)); - this.language$ = this.store.pipe(select(selectSettingsLanguage)); + this.isAuthenticated$ = this.store.select(selectIsAuthenticated); + this.stickyHeader$ = this.store.select(selectSettingsStickyHeader); + this.language$ = this.store.select(selectSettingsLanguage); this.theme$ = this.store.pipe(select(selectEffectiveTheme)); } From 91058b6adae4090ac8475efbd40177ab9d274c3f Mon Sep 17 00:00:00 2001 From: tetra Date: Sat, 18 Sep 2021 21:54:12 +0430 Subject: [PATCH 3/4] refactor: write a function that can return a tuple (a triplet, to be precise) of such actions docs: https://indepth.dev/posts/1451/ngrx-best-practices-new --- .../stock-market/stock-market.actions.ts | 10 +++++ .../app/shared/extension/createHTTPActions.ts | 40 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 projects/angular-ngrx-material-starter/src/app/shared/extension/createHTTPActions.ts diff --git a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.ts b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.ts index 87df41427..8235553d7 100755 --- a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.ts +++ b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.ts @@ -2,6 +2,7 @@ import { createAction, props } from '@ngrx/store'; import { HttpErrorResponse } from '@angular/common/http'; import { Stock } from './stock-market.model'; +import { createHTTPActions } from '../../../shared/extension/createHTTPActions'; export const actionStockMarketRetrieve = createAction( '[Stock] Retrieve', @@ -17,3 +18,12 @@ export const actionStockMarketRetrieveError = createAction( '[Stock] Retrieve Error', props<{ error: HttpErrorResponse }>() ); + +// TODO: A way to create generic action +export const [ + actionStockMarketRetrieve2, + actionStockMarketRetrieveSuccess2, + actionStockMarketRetrieveError2 +] = createHTTPActions<'Parameters', Stock, HttpErrorResponse>( + '[Stock] Retrieve' +); diff --git a/projects/angular-ngrx-material-starter/src/app/shared/extension/createHTTPActions.ts b/projects/angular-ngrx-material-starter/src/app/shared/extension/createHTTPActions.ts new file mode 100644 index 000000000..197d3aa1c --- /dev/null +++ b/projects/angular-ngrx-material-starter/src/app/shared/extension/createHTTPActions.ts @@ -0,0 +1,40 @@ +import { HttpErrorResponse } from '@angular/common/http'; +import { ActionCreator, createAction } from '@ngrx/store'; +import { TypedAction } from '@ngrx/store/src/models'; + +export function createHTTPActions< + RequestPayload = void, + ResponsePayload = void, + ErrorPayload = HttpErrorResponse +>( + actionType: string +): [ + ActionCreator< + string, + (props?: RequestPayload) => { + payload?: RequestPayload; + } & TypedAction + >, + ActionCreator< + string, + (props?: ResponsePayload) => { + payload?: ResponsePayload; + } & TypedAction + >, + ActionCreator< + string, + (props?: ErrorPayload) => { + payload?: ErrorPayload; + } & TypedAction + > +] { + return [ + createAction(actionType, (payload?: RequestPayload) => ({ payload })), + createAction(`${actionType} Success`, (payload?: ResponsePayload) => ({ + payload + })), + createAction(`${actionType} Error`, (payload?: ErrorPayload) => ({ + payload + })) + ]; +} From 7465e2e6ff66477cf7fe7be7bf19b62ed88cadff Mon Sep 17 00:00:00 2001 From: tetra Date: Sun, 19 Sep 2021 13:54:53 +0430 Subject: [PATCH 4/4] refactor: very neat manner to instantly create three actions for a single HTTP call https://indepth.dev/posts/1451/ngrx-best-practices-new --- .../stock-market/stock-market.actions.spec.ts | 6 ++-- .../stock-market/stock-market.actions.ts | 29 +++---------------- .../stock-market/stock-market.effects.ts | 4 +-- .../stock-market/stock-market.reducer.spec.ts | 2 +- .../stock-market/stock-market.reducer.ts | 12 ++++---- .../app/shared/extension/createHTTPActions.ts | 12 ++++---- 6 files changed, 23 insertions(+), 42 deletions(-) diff --git a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.spec.ts b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.spec.ts index e2a16c6dd..08b1d7a71 100644 --- a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.spec.ts +++ b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.spec.ts @@ -12,7 +12,7 @@ describe('Stock Market Actions', () => { it('should create StockMarketRetrieve action', () => { const action = actionStockMarketRetrieve({ symbol }); expect(action.type).toEqual(actionStockMarketRetrieve.type); - expect(action.symbol).toEqual(symbol); + expect(action.payload.symbol).toEqual(symbol); }); it('should create StockMarketRetrieveSuccess action', () => { @@ -28,7 +28,7 @@ describe('Stock Market Actions', () => { }; const action = actionStockMarketRetrieveSuccess({ stock }); expect(action.type).toEqual(actionStockMarketRetrieveSuccess.type); - expect(action.stock).toEqual( + expect(action.payload?.stock).toEqual( jasmine.objectContaining({ ...stock }) @@ -40,6 +40,6 @@ describe('Stock Market Actions', () => { const action = actionStockMarketRetrieveError({ error: error }); expect(action.type).toEqual(actionStockMarketRetrieveError.type); - expect(action.error).toEqual(error); + expect(action.payload.error).toEqual(error); }); }); diff --git a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.ts b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.ts index 8235553d7..d2f42553a 100755 --- a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.ts +++ b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.actions.ts @@ -1,29 +1,8 @@ -import { createAction, props } from '@ngrx/store'; -import { HttpErrorResponse } from '@angular/common/http'; - import { Stock } from './stock-market.model'; import { createHTTPActions } from '../../../shared/extension/createHTTPActions'; -export const actionStockMarketRetrieve = createAction( - '[Stock] Retrieve', - props<{ symbol: string }>() -); - -export const actionStockMarketRetrieveSuccess = createAction( - '[Stock] Retrieve Success', - props<{ stock: Stock }>() -); - -export const actionStockMarketRetrieveError = createAction( - '[Stock] Retrieve Error', - props<{ error: HttpErrorResponse }>() -); - -// TODO: A way to create generic action export const [ - actionStockMarketRetrieve2, - actionStockMarketRetrieveSuccess2, - actionStockMarketRetrieveError2 -] = createHTTPActions<'Parameters', Stock, HttpErrorResponse>( - '[Stock] Retrieve' -); + actionStockMarketRetrieve, + actionStockMarketRetrieveSuccess, + actionStockMarketRetrieveError +] = createHTTPActions<{ symbol: string }, { stock: Stock }>('[Stock] Retrieve'); diff --git a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.effects.ts b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.effects.ts index 8916d3e0a..498d71031 100755 --- a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.effects.ts +++ b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.effects.ts @@ -23,12 +23,12 @@ export class StockMarketEffects { ofType(actionStockMarketRetrieve), tap((action) => this.localStorageService.setItem(STOCK_MARKET_KEY, { - symbol: action.symbol + symbol: action.payload.symbol }) ), debounceTime(debounce), switchMap((action) => - this.service.retrieveStock(action.symbol).pipe( + this.service.retrieveStock(action.payload.symbol).pipe( map((stock) => actionStockMarketRetrieveSuccess({ stock })), catchError((error) => of(actionStockMarketRetrieveError({ error })) diff --git a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.reducer.spec.ts b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.reducer.spec.ts index 66f14c224..73c0fd40e 100755 --- a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.reducer.spec.ts +++ b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.reducer.spec.ts @@ -51,7 +51,7 @@ describe('StockMarketReducer', () => { expect(state.loading).toBeTruthy(); expect(state.stock).toBeUndefined(); expect(state.error).toBeUndefined(); - expect(state.symbol).toBe(action.symbol); + expect(state.symbol).toBe(action.payload.symbol); }); }); diff --git a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.reducer.ts b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.reducer.ts index d3d3c1e07..df4279b0e 100755 --- a/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.reducer.ts +++ b/projects/angular-ngrx-material-starter/src/app/features/examples/stock-market/stock-market.reducer.ts @@ -13,24 +13,24 @@ export const initialState: StockMarketState = { const reducer = createReducer( initialState, - on(actionStockMarketRetrieve, (state, { symbol }) => ({ + on(actionStockMarketRetrieve, (state, { payload }) => ({ ...state, loading: true, stock: undefined, error: undefined, - symbol + symbol: payload.symbol })), - on(actionStockMarketRetrieveSuccess, (state, { stock }) => ({ + on(actionStockMarketRetrieveSuccess, (state, { payload }) => ({ ...state, loading: false, - stock, + stock: payload?.stock, error: undefined })), - on(actionStockMarketRetrieveError, (state, { error }) => ({ + on(actionStockMarketRetrieveError, (state, { payload }) => ({ ...state, loading: false, stock: undefined, - error + error: payload?.error })) ); diff --git a/projects/angular-ngrx-material-starter/src/app/shared/extension/createHTTPActions.ts b/projects/angular-ngrx-material-starter/src/app/shared/extension/createHTTPActions.ts index 197d3aa1c..fc33bb4ce 100644 --- a/projects/angular-ngrx-material-starter/src/app/shared/extension/createHTTPActions.ts +++ b/projects/angular-ngrx-material-starter/src/app/shared/extension/createHTTPActions.ts @@ -5,19 +5,19 @@ import { TypedAction } from '@ngrx/store/src/models'; export function createHTTPActions< RequestPayload = void, ResponsePayload = void, - ErrorPayload = HttpErrorResponse + ErrorPayload = any | HttpErrorResponse >( actionType: string ): [ ActionCreator< string, - (props?: RequestPayload) => { - payload?: RequestPayload; + (props: RequestPayload) => { + payload: RequestPayload; } & TypedAction >, ActionCreator< string, - (props?: ResponsePayload) => { + (props: ResponsePayload) => { payload?: ResponsePayload; } & TypedAction >, @@ -29,7 +29,9 @@ export function createHTTPActions< > ] { return [ - createAction(actionType, (payload?: RequestPayload) => ({ payload })), + createAction(actionType, (payload: RequestPayload) => ({ + payload: payload + })), createAction(`${actionType} Success`, (payload?: ResponsePayload) => ({ payload })),