diff --git a/.eslintrc b/.eslintrc index 8324f13f..474e65e6 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 a77483f5..f97a18ee 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", 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 a7a1547a..120bea85 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)); } 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 e2a16c6d..08b1d7a7 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 87df4142..d2f42553 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,19 +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 }>() -); +export const [ + 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 8916d3e0..498d7103 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 66f14c22..73c0fd40 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 d3d3c1e0..df4279b0 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 new file mode 100644 index 00000000..fc33bb4c --- /dev/null +++ b/projects/angular-ngrx-material-starter/src/app/shared/extension/createHTTPActions.ts @@ -0,0 +1,42 @@ +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 = any | 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: payload + })), + createAction(`${actionType} Success`, (payload?: ResponsePayload) => ({ + payload + })), + createAction(`${actionType} Error`, (payload?: ErrorPayload) => ({ + payload + })) + ]; +}