From 299ac4928846fd446ddf29584c5daa8bd55d57dd Mon Sep 17 00:00:00 2001 From: Wes Grimes Date: Tue, 10 Sep 2019 17:54:03 -0400 Subject: [PATCH] refactor: migrate to NgRx v8 creators thanks --- .vscode/settings.json | 11 +- package-lock.json | 239 +++++++++++++----- package.json | 4 +- src/app/core/toast.service.ts | 2 +- src/app/material/material.module.ts | 20 +- src/app/store/actions/data.actions.ts | 18 +- src/app/store/actions/hero.actions.ts | 171 ++++--------- src/app/store/actions/villain.actions.ts | 175 +++++-------- src/app/store/app-store.module.ts | 12 +- src/app/store/effects/hero.effects.ts | 79 +++--- src/app/store/effects/villain.effects.ts | 77 +++--- src/app/store/reducers/hero.reducer.ts | 162 +++++------- src/app/store/reducers/index.ts | 11 +- src/app/store/reducers/villain.reducer.ts | 162 +++++------- .../services/hero-http-dispatchers.service.ts | 24 +- src/app/store/services/hero.dispatchers.ts | 11 +- src/app/store/services/villain.dispatchers.ts | 11 +- 17 files changed, 539 insertions(+), 650 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b990796..0f87e27 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,6 +11,11 @@ "titleBar.inactiveForeground": "#e7e7e799", "statusBar.background": "#6b6bbc", "statusBarItem.hoverBackground": "#8e8ecc", - "statusBar.foreground": "#e7e7e7" - } -} \ No newline at end of file + "statusBar.foreground": "#e7e7e7", + "panel.border": "#8e8ecc", + "sideBar.border": "#8e8ecc", + "editorGroup.border": "#8e8ecc", + "tab.activeBorder": "#8e8ecc" + }, + "peacock.color": "6b6bbc" +} diff --git a/package-lock.json b/package-lock.json index b1abb41..a032335 100644 --- a/package-lock.json +++ b/package-lock.json @@ -261,9 +261,9 @@ } }, "@angular/cdk": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.3.7.tgz", - "integrity": "sha512-xbXxhHHKGkVuW6K7pzPmvpJXIwpl0ykBnvA2g+/7Sgy5Pd35wCC+UtHD9RYczDM/mkygNxMQtagyCErwFnDtQA==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-8.1.1.tgz", + "integrity": "sha512-5hBmhrHf9+WjGVIT8gbhT0Nh37BAjgI2TGRkt1o4qX8cG+1B6gU2MxM+CDJ7PhxSJi9lW93lq2AMuWwnRSllyg==", "requires": { "parse5": "^5.0.0", "tslib": "^1.7.1" @@ -525,9 +525,9 @@ "dev": true }, "@angular/material": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-7.3.7.tgz", - "integrity": "sha512-Eq+7frkeNGkLOfEtmkmJgR+AgoWajOipXZWWfCSamNfpCcPof82DwvGOpAmgGni9FuN2XFQdqP5MoaffQzIvUA==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-8.1.1.tgz", + "integrity": "sha512-45aaxKuLTrthzhAhG2+OY86wafuRBteZcRjDG7rKZ3Cc3KteUp5QwAi+QbhHzs4O3WXLWTAmuLYJelRqRqqw7g==", "requires": { "tslib": "^1.7.1" } @@ -2164,24 +2164,110 @@ "dev": true }, "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.0.2.tgz", + "integrity": "sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "^3.0.1", + "braces": "^3.0.2", + "fsevents": "^2.0.6", + "glob-parent": "^5.0.0", + "is-binary-path": "^2.1.0", + "is-glob": "^4.0.1", + "normalize-path": "^3.0.0", + "readdirp": "^3.1.1" + }, + "dependencies": { + "anymatch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.0.tgz", + "integrity": "sha512-Ozz7l4ixzI7Oxj2+cw+p0tVUt27BpaJ+1+q1TCeANWxHpvyn2+Un+YamBdfKu0uh8xLodGhoa1v7595NhKDAuA==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz", + "integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", + "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "readdirp": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.1.2.tgz", + "integrity": "sha512-8rhl0xs2cxfVsqzreYCvs8EwBfn/DhVdqtoLmw19uI3SC5avYX9teCurlErfpPXGmYtMHReGaP2RsLnFvz/lnw==", + "dev": true, + "requires": { + "picomatch": "^2.0.4" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } } }, "chownr": { @@ -6033,12 +6119,38 @@ "useragent": "2.3.0" }, "dependencies": { + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, "mime": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", "dev": true }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6214,12 +6326,6 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, "lodash.tail": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", @@ -6573,9 +6679,9 @@ } }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -7504,6 +7610,12 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "picomatch": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", + "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "dev": true + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -8659,9 +8771,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -9865,38 +9977,15 @@ "dev": true }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unique-filename": { @@ -10152,6 +10241,34 @@ "chokidar": "^2.0.2", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0" + }, + "dependencies": { + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } } }, "wbuf": { diff --git a/package.json b/package.json index 4ff31a4..61766c1 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,12 @@ "private": true, "dependencies": { "@angular/animations": "~8.1.2", - "@angular/cdk": "^7.2.0", + "@angular/cdk": "^8.1.1", "@angular/common": "~8.1.2", "@angular/compiler": "~8.1.2", "@angular/core": "~8.1.2", "@angular/forms": "~8.1.2", - "@angular/material": "^7.2.0", + "@angular/material": "^8.1.1", "@angular/platform-browser": "~8.1.2", "@angular/platform-browser-dynamic": "~8.1.2", "@angular/router": "~8.1.2", diff --git a/src/app/core/toast.service.ts b/src/app/core/toast.service.ts index f1d5746..4cf5369 100644 --- a/src/app/core/toast.service.ts +++ b/src/app/core/toast.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { MatSnackBar } from '@angular/material'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { isE2E } from './e2e-check'; @Injectable() diff --git a/src/app/material/material.module.ts b/src/app/material/material.module.ts index 0cc21ec..f6d667a 100644 --- a/src/app/material/material.module.ts +++ b/src/app/material/material.module.ts @@ -1,15 +1,13 @@ import { NgModule } from '@angular/core'; -import { - MatButtonModule, - MatCardModule, - MatIconModule, - MatProgressSpinnerModule, - MatSlideToggleModule, - MatSnackBarModule, - MatTooltipModule, - MatToolbarModule, - MatInputModule -} from '@angular/material'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatTooltipModule } from '@angular/material/tooltip'; @NgModule({ imports: [ diff --git a/src/app/store/actions/data.actions.ts b/src/app/store/actions/data.actions.ts index 7ec18ef..753f345 100644 --- a/src/app/store/actions/data.actions.ts +++ b/src/app/store/actions/data.actions.ts @@ -1,27 +1,17 @@ // General purpose entity action stuff, good for any entity type -import { Action } from '@ngrx/store'; +import { ActionCreator } from '@ngrx/store'; import { Observable, of } from 'rxjs'; import { catchError, mergeMap } from 'rxjs/operators'; import { DataServiceError } from '../config'; -export abstract class DataAction implements Action { - readonly type: string; - constructor(public readonly payload: T) {} -} - -export abstract class DataErrorAction implements Action { - readonly type: string; - constructor(public readonly payload: DataServiceError) {} -} - // Function of additional success actions // that returns a function that returns // an observable of ngrx action(s) from DataService method observable -export const toAction = (...actions: Action[]) => ( +export const toAction = (...actions: ActionCreator[]) => ( source: Observable, - successAction: new (data: T) => Action, - errorAction: new (err: DataServiceError) => Action + successAction: new (data: T) => any, + errorAction: new (err: DataServiceError) => any ) => source.pipe( mergeMap((data: T) => [new successAction(data), ...actions]), diff --git a/src/app/store/actions/hero.actions.ts b/src/app/store/actions/hero.actions.ts index 11f2292..4328ad5 100644 --- a/src/app/store/actions/hero.actions.ts +++ b/src/app/store/actions/hero.actions.ts @@ -1,123 +1,54 @@ -import { Action } from '@ngrx/store'; - +import { createAction, props } from '@ngrx/store'; import { Hero } from '../../core'; import { DataServiceError } from '../config'; -import { DataAction, DataErrorAction } from './data.actions'; -export const ADD_HERO = '[Hero] ADD_HERO'; -export const ADD_HERO_ERROR = '[Hero] ADD_HERO_ERROR'; -export const ADD_HERO_SUCCESS = '[Hero] ADD_HERO_SUCCESS'; - -export const GET_HERO = '[Hero] GET_HERO'; -export const GET_HERO_SUCCESS = '[Hero] GET_HERO_SUCCESS'; -export const GET_HERO_ERROR = '[Hero] GET_HERO_ERROR'; - -export const UPDATE_HERO = '[Hero] UPDATE_HERO'; -export const UPDATE_HERO_SUCCESS = '[Hero] UPDATE_HERO_SUCCESS'; -export const UPDATE_HERO_ERROR = '[Hero] UPDATE_HERO_ERROR'; - -export const GET_HEROES = '[Hero] GET_HEROES'; -export const GET_HEROES_SUCCESS = '[Hero] GET_HEROES_SUCCESS'; -export const GET_HEROES_ERROR = '[Hero] GET_HEROES_ERROR'; - -export const DELETE_HERO = '[Hero] DELETE_HERO'; -export const DELETE_HERO_SUCCESS = '[Hero] DELETE_HERO_SUCCESS'; -export const DELETE_HERO_ERROR = '[Hero] DELETE_HERO_ERROR'; - -export const SET_HERO_LOADING = '[Hero] SET_HERO_LOADING'; - -export abstract class HeroAction implements DataAction { - readonly type: string; - constructor(public readonly payload: Hero) {} -} - -export abstract class HeroErrorAction implements DataErrorAction { - readonly type: string; - constructor(public readonly payload: DataServiceError) {} -} - -export class GetHeroes implements Action { - readonly type = GET_HEROES; -} - -export class GetHeroesSuccess implements Action { - readonly type = GET_HEROES_SUCCESS; - constructor(public readonly payload: Hero[]) {} -} - -export class GetHeroesError implements Action { - readonly type = GET_HEROES_ERROR; - constructor(public readonly payload: any) {} -} - -export class AddHero extends HeroAction { - readonly type = ADD_HERO; -} - -export class AddHeroSuccess extends HeroAction { - readonly type = ADD_HERO_SUCCESS; -} - -export class AddHeroError extends HeroErrorAction { - readonly type = ADD_HERO_ERROR; -} - -export class GetHero implements Action { - readonly type = GET_HERO; - constructor(public readonly payload: string) {} -} - -export class GetHeroSuccess extends HeroAction { - readonly type = GET_HERO_SUCCESS; -} - -export class GetHeroError extends HeroErrorAction { - readonly type = GET_HERO_ERROR; -} - -export class UpdateHero extends HeroAction { - readonly type = UPDATE_HERO; -} - -export class UpdateHeroSuccess extends HeroAction { - readonly type = UPDATE_HERO_SUCCESS; -} - -export class UpdateHeroError extends HeroErrorAction { - readonly type = UPDATE_HERO_ERROR; -} - -export class DeleteHero extends HeroAction { - readonly type = DELETE_HERO; -} - -export class DeleteHeroSuccess extends HeroAction { - readonly type = DELETE_HERO_SUCCESS; -} - -export class DeleteHeroError extends HeroErrorAction { - readonly type = DELETE_HERO_ERROR; -} - -export class SetHeroLoading { - readonly type = SET_HERO_LOADING; - constructor(public payload = true) {} -} - -export type AllHeroActions = - | GetHero - | GetHeroSuccess - | GetHeroError - | UpdateHero - | UpdateHeroSuccess - | UpdateHeroError - | GetHeroes - | GetHeroesSuccess - | GetHeroesError - | AddHero - | AddHeroSuccess - | AddHeroError - | DeleteHero - | DeleteHeroSuccess - | DeleteHeroError - | SetHeroLoading; +export const createHeroAction = (actionType: string) => + createAction(actionType, props<{ hero: Hero }>()); + +export const createHeroErrorAction = (actionType: string) => + createAction(actionType, props<{ error: DataServiceError }>()); + +export const getHeroes = createAction('[Hero] GET_HEROES'); + +export const getHeroesSuccess = createAction( + '[Hero] GET_HEROES_SUCCESS', + props<{ heroes: Hero[] }>() +); + +export const getHeroesError = createAction( + '[Hero] GET_HEROES_ERROR', + props<{ error: any }>() +); + +export const addHero = createHeroAction('[Hero] ADD_HERO'); + +export const addHeroSuccess = createHeroAction('[Hero] ADD_HERO_SUCCESS'); + +export const addHeroError = createHeroErrorAction('[Hero] ADD_HERO_ERROR'); + +export const getHero = createAction('[Hero] GET_HERO', props<{ id: string }>()); + +export const getHeroSuccess = createHeroAction('[Hero] GET_HERO_SUCCESS'); + +export const getHeroError = createHeroErrorAction('[Hero] GET_HERO_ERROR'); + +export const updateHero = createHeroAction('[Hero] UPDATE_HERO'); + +export const updateHeroSuccess = createHeroAction('[Hero] UPDATE_HERO_SUCCESS'); + +export const updateHeroError = createHeroErrorAction( + '[Hero] UPDATE_HERO_ERROR' +); + +export const deleteHero = createHeroAction('[Hero] DELETE_HERO'); + +export const deleteHeroSuccess = createHeroAction('[Hero] DELETE_HERO_SUCCESS'); + +export const deleteHeroError = createHeroErrorAction( + '[Hero] DELETE_HERO_ERROR' +); + +export const setHeroLoading = createAction( + '[Hero] SET_HERO_LOADING', + props<{ loading: boolean }>() +); diff --git a/src/app/store/actions/villain.actions.ts b/src/app/store/actions/villain.actions.ts index b255c00..94b83cc 100644 --- a/src/app/store/actions/villain.actions.ts +++ b/src/app/store/actions/villain.actions.ts @@ -1,115 +1,64 @@ -import { Action } from '@ngrx/store'; - +import { createAction, props } from '@ngrx/store'; import { Villain } from '../../core'; import { DataServiceError } from '../config'; -import { DataAction, DataErrorAction } from './data.actions'; - -export const ADD_VILLAIN = '[Villain] ADD_VILLAIN'; -export const ADD_VILLAIN_ERROR = '[Villain] ADD_VILLAIN_ERROR'; -export const ADD_VILLAIN_SUCCESS = '[Villain] ADD_VILLAIN_SUCCESS'; - -export const GET_VILLAIN = '[Villain] GET_VILLAIN'; -export const GET_VILLAIN_SUCCESS = '[Villain] GET_VILLAIN_SUCCESS'; -export const GET_VILLAIN_ERROR = '[Villain] GET_VILLAIN_ERROR'; - -export const UPDATE_VILLAIN = '[Villain] UPDATE_VILLAIN'; -export const UPDATE_VILLAIN_SUCCESS = '[Villain] UPDATE_VILLAIN_SUCCESS'; -export const UPDATE_VILLAIN_ERROR = '[Villain] UPDATE_VILLAIN_ERROR'; - -export const GET_VILLAINS = '[Villain] GET_VILLAINS'; -export const GET_VILLAINS_SUCCESS = '[Villain] GET_VILLAINS_SUCCESS'; -export const GET_VILLAINS_ERROR = '[Villain] GET_VILLAINS_ERROR'; - -export const DELETE_VILLAIN = '[Villain] DELETE_VILLAIN'; -export const DELETE_VILLAIN_SUCCESS = '[Villain] DELETE_VILLAIN_SUCCESS'; -export const DELETE_VILLAIN_ERROR = '[Villain] DELETE_VILLAIN_ERROR'; - -export abstract class VillainAction implements DataAction { - readonly type: string; - constructor(public readonly payload: Villain) {} -} - -export abstract class VillainErrorAction implements DataErrorAction { - readonly type: string; - constructor(public readonly payload: DataServiceError) {} -} - -export class GetVillains implements Action { - readonly type = GET_VILLAINS; -} - -export class GetVillainsSuccess implements Action { - readonly type = GET_VILLAINS_SUCCESS; - constructor(public readonly payload: Villain[]) {} -} - -export class GetVillainsError implements Action { - readonly type = GET_VILLAINS_ERROR; - constructor(public readonly payload: any) {} -} - -export class AddVillain extends VillainAction { - readonly type = ADD_VILLAIN; -} - -export class AddVillainSuccess extends VillainAction { - readonly type = ADD_VILLAIN_SUCCESS; -} - -export class AddVillainError extends VillainErrorAction { - readonly type = ADD_VILLAIN_ERROR; -} - -export class GetVillain implements Action { - readonly type = GET_VILLAIN; - constructor(public readonly payload: string) {} -} - -export class GetVillainSuccess extends VillainAction { - readonly type = GET_VILLAIN_SUCCESS; -} - -export class GetVillainError extends VillainErrorAction { - readonly type = GET_VILLAIN_ERROR; -} - -export class UpdateVillain extends VillainAction { - readonly type = UPDATE_VILLAIN; -} - -export class UpdateVillainSuccess extends VillainAction { - readonly type = UPDATE_VILLAIN_SUCCESS; -} - -export class UpdateVillainError extends VillainErrorAction { - readonly type = UPDATE_VILLAIN_ERROR; -} - -export class DeleteVillain extends VillainAction { - readonly type = DELETE_VILLAIN; -} - -export class DeleteVillainSuccess extends VillainAction { - readonly type = DELETE_VILLAIN_SUCCESS; -} - -export class DeleteVillainError extends VillainErrorAction { - readonly type = DELETE_VILLAIN_ERROR; -} - -export type AllVillainActions = - | GetVillain - | GetVillainSuccess - | GetVillainError - | UpdateVillain - | UpdateVillainSuccess - | UpdateVillainError - | GetVillains - | GetVillainsSuccess - | GetVillainsError - | AddVillain - | AddVillainSuccess - | AddVillainError - | DeleteVillain - | DeleteVillainSuccess - | DeleteVillainError; + +export const createVillainAction = (actionType: string) => + createAction(actionType, props<{ villain: Villain }>()); + +export const createVillainErrorAction = (actionType: string) => + createAction(actionType, props<{ error: DataServiceError }>()); + +export const getVillains = createAction('[Villain] GET_VILLAINS'); + +export const getVillainsSuccess = createAction( + '[Villain] GET_VILLAINS_SUCCESS', + props<{ villains: Villain[] }>() +); + +export const getVillainsError = createAction( + '[Villain] GET_VILLAINS_ERROR', + props<{ error: any }>() +); + +export const addVillain = createVillainAction('[Villain] ADD_VILLAIN'); + +export const addVillainSuccess = createVillainAction( + '[Villain] ADD_VILLAIN_SUCCESS' +); + +export const addVillainError = createVillainErrorAction( + '[Villain] ADD_VILLAIN_ERROR' +); + +export const getVillain = createAction( + '[Villain] GET_VILLAIN', + props<{ id: string }>() +); + +export const getVillainSuccess = createVillainAction( + '[Villain] GET_VILLAIN_SUCCESS' +); + +export const getVillainError = createVillainErrorAction( + '[Villain] GET_VILLAIN_ERROR' +); + +export const updateVillain = createVillainAction('[Villain] UPDATE_VILLAIN'); + +export const updateVillainSuccess = createVillainAction( + '[Villain] UPDATE_VILLAIN_SUCCESS' +); + +export const updateVillainError = createVillainErrorAction( + '[Villain] UPDATE_VILLAIN_ERROR' +); + +export const deleteVillain = createVillainAction('[Villain] DELETE_VILLAIN'); + +export const deleteVillainSuccess = createVillainAction( + '[Villain] DELETE_VILLAIN_SUCCESS' +); + +export const deleteVillainError = createVillainErrorAction( + '[Villain] DELETE_VILLAIN_ERROR' +); diff --git a/src/app/store/app-store.module.ts b/src/app/store/app-store.module.ts index 6b1b9e7..4b3554d 100644 --- a/src/app/store/app-store.module.ts +++ b/src/app/store/app-store.module.ts @@ -1,15 +1,11 @@ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpClientModule } from '@angular/common/http'; -import { FormsModule } from '@angular/forms'; -import { StoreModule, MetaReducer } from '@ngrx/store'; +import { NgModule } from '@angular/core'; import { EffectsModule } from '@ngrx/effects'; -import { StoreDevtoolsModule } from '@ngrx/store-devtools'; - -import { reducers } from './reducers'; -import { effects } from './effects'; +import { StoreModule } from '@ngrx/store'; import { services } from './'; -import { environment } from '../../environments/environment'; +import { effects } from './effects'; +import { reducers } from './reducers'; @NgModule({ imports: [ diff --git a/src/app/store/effects/hero.effects.ts b/src/app/store/effects/hero.effects.ts index cc3aaa9..1cf1374 100644 --- a/src/app/store/effects/hero.effects.ts +++ b/src/app/store/effects/hero.effects.ts @@ -1,70 +1,61 @@ import { Injectable } from '@angular/core'; -import { Actions, Effect, ofType } from '@ngrx/effects'; -import { Action, Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; -import { concatMap, switchMap } from 'rxjs/operators'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { of } from 'rxjs'; +import { catchError, concatMap, map, switchMap } from 'rxjs/operators'; import * as HeroActions from '../actions'; import { HeroDataService } from '../services'; -import { EntityState } from '../reducers'; - -const filterAction = new HeroActions.GetHeroes(); -const toAction = HeroActions.toAction(); -type HeroAction = HeroActions.HeroAction; @Injectable() export class HeroEffects { - @Effect() - // this.actions.pipe( - // ofType(AuthenticationActionTypes.LOGIN) - getHeroes$: Observable = this.actions$.pipe( - ofType(HeroActions.GET_HEROES), - switchMap(() => - toAction( - this.heroDataService.getHeroes(), - HeroActions.GetHeroesSuccess, - HeroActions.GetHeroesError + getHeroes$ = createEffect(() => + this.actions$.pipe( + ofType(HeroActions.getHeroes), + switchMap(() => + this.heroDataService.getHeroes().pipe( + map(heroes => HeroActions.getHeroesSuccess({ heroes })), + catchError(error => of(HeroActions.getHeroesError({ error }))) + ) ) ) ); - @Effect() - addHero$: Observable = this.actions$.pipe( - ofType(HeroActions.ADD_HERO), - concatMap((action: HeroAction) => - toAction( - this.heroDataService.addHero(action.payload), - HeroActions.AddHeroSuccess, - HeroActions.AddHeroError + addHero$ = createEffect(() => + this.actions$.pipe( + ofType(HeroActions.addHero), + concatMap(action => + this.heroDataService.addHero(action.hero).pipe( + map(hero => HeroActions.addHeroSuccess({ hero })), + catchError(error => of(HeroActions.addHeroError({ error }))) + ) ) ) ); - @Effect() - deleteHero$: Observable = this.actions$.pipe( - ofType(HeroActions.DELETE_HERO), - concatMap((action: HeroAction) => - toAction( - this.heroDataService.deleteHero(action.payload), - HeroActions.DeleteHeroSuccess, - HeroActions.DeleteHeroError + deleteHero$ = createEffect(() => + this.actions$.pipe( + ofType(HeroActions.deleteHero), + concatMap(action => + this.heroDataService.deleteHero(action.hero).pipe( + map(hero => HeroActions.deleteHeroSuccess({ hero })), + catchError(error => of(HeroActions.deleteHeroError({ error }))) + ) ) ) ); - @Effect() - updateHero$: Observable = this.actions$.pipe( - ofType(HeroActions.UPDATE_HERO), - concatMap((action: HeroAction) => - toAction( - this.heroDataService.updateHero(action.payload), - HeroActions.UpdateHeroSuccess, - HeroActions.UpdateHeroError + updateHero$ = createEffect(() => + this.actions$.pipe( + ofType(HeroActions.updateHero), + concatMap(action => + this.heroDataService.updateHero(action.hero).pipe( + map(hero => HeroActions.updateHeroSuccess({ hero })), + catchError(error => of(HeroActions.updateHeroError({ error }))) + ) ) ) ); constructor( - private store: Store, private actions$: Actions, private heroDataService: HeroDataService ) {} diff --git a/src/app/store/effects/villain.effects.ts b/src/app/store/effects/villain.effects.ts index 3315776..0d1154e 100644 --- a/src/app/store/effects/villain.effects.ts +++ b/src/app/store/effects/villain.effects.ts @@ -1,68 +1,61 @@ import { Injectable } from '@angular/core'; -import { Actions, Effect, ofType } from '@ngrx/effects'; -import { Action, Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; -import { concatMap, switchMap } from 'rxjs/operators'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { of } from 'rxjs'; +import { catchError, concatMap, map, switchMap } from 'rxjs/operators'; import * as VillainActions from '../actions'; -import { EntityState } from '../reducers'; import { VillainDataService } from '../services'; -const filterAction = new VillainActions.GetVillains(); -const toAction = VillainActions.toAction(); -type VillainAction = VillainActions.VillainAction; - @Injectable() export class VillainEffects { - @Effect() - getVillains$: Observable = this.actions$.pipe( - ofType(VillainActions.GET_VILLAINS), - switchMap(() => - toAction( - this.villainDataService.getVillains(), - VillainActions.GetVillainsSuccess, - VillainActions.GetVillainsError + getVillains$ = createEffect(() => + this.actions$.pipe( + ofType(VillainActions.getVillains), + switchMap(() => + this.villainDataService.getVillains().pipe( + map(villains => VillainActions.getVillainsSuccess({ villains })), + catchError(error => of(VillainActions.getVillainsError({ error }))) + ) ) ) ); - @Effect() - addVillain$: Observable = this.actions$.pipe( - ofType(VillainActions.ADD_VILLAIN), - concatMap((action: VillainAction) => - toAction( - this.villainDataService.addVillain(action.payload), - VillainActions.AddVillainSuccess, - VillainActions.AddVillainError + addVillain$ = createEffect(() => + this.actions$.pipe( + ofType(VillainActions.addVillain), + concatMap(action => + this.villainDataService.addVillain(action.villain).pipe( + map(villain => VillainActions.addVillainSuccess({ villain })), + catchError(error => of(VillainActions.addVillainError({ error }))) + ) ) ) ); - @Effect() - deleteVillain$: Observable = this.actions$.pipe( - ofType(VillainActions.DELETE_VILLAIN), - concatMap((action: VillainAction) => - toAction( - this.villainDataService.deleteVillain(action.payload), - VillainActions.DeleteVillainSuccess, - VillainActions.DeleteVillainError + deleteVillain$ = createEffect(() => + this.actions$.pipe( + ofType(VillainActions.deleteVillain), + concatMap(action => + this.villainDataService.deleteVillain(action.villain).pipe( + map(villain => VillainActions.deleteVillainSuccess({ villain })), + catchError(error => of(VillainActions.deleteVillainError({ error }))) + ) ) ) ); - @Effect() - updateVillain$: Observable = this.actions$.pipe( - ofType(VillainActions.UPDATE_VILLAIN), - concatMap((action: VillainAction) => - toAction( - this.villainDataService.updateVillain(action.payload), - VillainActions.UpdateVillainSuccess, - VillainActions.UpdateVillainError + updateVillain$ = createEffect(() => + this.actions$.pipe( + ofType(VillainActions.updateVillain), + concatMap(action => + this.villainDataService.updateVillain(action.villain).pipe( + map(villain => VillainActions.updateVillainSuccess({ villain })), + catchError(error => of(VillainActions.updateVillainError({ error }))) + ) ) ) ); constructor( - private store: Store, private actions$: Actions, private villainDataService: VillainDataService ) {} diff --git a/src/app/store/reducers/hero.reducer.ts b/src/app/store/reducers/hero.reducer.ts index 641e65d..5434204 100644 --- a/src/app/store/reducers/hero.reducer.ts +++ b/src/app/store/reducers/hero.reducer.ts @@ -1,3 +1,4 @@ +import { Action, createReducer, on } from '@ngrx/store'; import { Hero } from '../../core'; import * as HeroActions from '../actions'; @@ -13,107 +14,6 @@ export const initialState: HeroState = { error: false }; -export function reducer( - state = initialState, - action: HeroActions.AllHeroActions -): HeroState { - switch (action.type) { - case HeroActions.ADD_HERO: { - return { ...state, loading: true }; - } - - case HeroActions.ADD_HERO_SUCCESS: { - return { - ...state, - loading: false, - heroes: [...state.heroes, { ...action.payload }] - }; - } - - case HeroActions.ADD_HERO_ERROR: { - return { ...state, loading: false }; - } - - case HeroActions.GET_HEROES: { - return { ...state, loading: true }; - } - - case HeroActions.GET_HEROES_ERROR: { - return { - ...state, - loading: false - }; - } - - case HeroActions.GET_HEROES_SUCCESS: { - return { - ...state, - heroes: action.payload, - loading: false - }; - } - - case HeroActions.DELETE_HERO: { - return { - ...state, - loading: true, - heroes: state.heroes.filter(h => h !== action.payload) - }; - } - - case HeroActions.DELETE_HERO_SUCCESS: { - const result = { ...state, loading: false }; - return result; - } - - case HeroActions.DELETE_HERO_ERROR: { - return { - ...state, - heroes: [...state.heroes, action.payload.requestData], - loading: false - }; - } - - case HeroActions.UPDATE_HERO: { - return { - ...state, - heroes: state.heroes.map(h => { - if (h.id === action.payload.id) { - state.loading = true; - } - return h; - }) - }; - } - - case HeroActions.UPDATE_HERO_SUCCESS: { - return modifyHeroState(state, action.payload); - } - - case HeroActions.UPDATE_HERO_ERROR: { - return { - ...state, - loading: false, - heroes: state.heroes.map(h => { - if (h.id === action.payload.requestData.id) { - // Huh? No idea what the error is! - state.error = true; - } - return h; - }) - }; - } - - case HeroActions.SET_HERO_LOADING: { - return { - ...state, - loading: action.payload == null ? true : action.payload - }; - } - } - return state; -} - function modifyHeroState( heroState: HeroState, heroChanges: Partial @@ -130,3 +30,63 @@ function modifyHeroState( }) }; } + +const heroReducer = createReducer( + initialState, + on(HeroActions.addHero, state => ({ ...state, loading: true })), + on(HeroActions.addHeroSuccess, (state, { hero }) => ({ + ...state, + loading: false, + heroes: [...state.heroes, { ...hero }] + })), + on(HeroActions.addHeroError, state => ({ ...state, loading: false })), + on(HeroActions.getHeroes, state => ({ ...state, loading: true })), + on(HeroActions.getHeroesError, state => ({ ...state, loading: false })), + on(HeroActions.getHeroesSuccess, (state, { heroes }) => ({ + ...state, + loading: false, + heroes + })), + on(HeroActions.deleteHero, (state, { hero }) => ({ + ...state, + loading: false, + heroes: state.heroes.filter(h => h !== hero) + })), + on(HeroActions.deleteHeroSuccess, state => ({ ...state, loading: false })), + on(HeroActions.deleteHeroError, (state, { error }) => ({ + ...state, + heroes: [...state.heroes, error.requestData], + loading: false + })), + on(HeroActions.updateHero, (state, { hero }) => ({ + ...state, + heroes: state.heroes.map(h => { + if (h.id === hero.id) { + state.loading = true; + } + return h; + }) + })), + on(HeroActions.updateHeroSuccess, (state, { hero }) => + modifyHeroState(state, hero) + ), + on(HeroActions.updateHeroError, (state, { error }) => ({ + ...state, + heroes: state.heroes.map(h => { + if (h.id === error.requestData.id) { + // Huh? No idea what the error is! + state.error = true; + } + return h; + }), + loading: false + })), + on(HeroActions.setHeroLoading, (state, { loading }) => ({ + ...state, + loading: loading == null ? true : loading + })) +); + +export function reducer(state: HeroState | undefined, action: Action) { + return heroReducer(state, action); +} diff --git a/src/app/store/reducers/index.ts b/src/app/store/reducers/index.ts index 40afe22..a2027b8 100644 --- a/src/app/store/reducers/index.ts +++ b/src/app/store/reducers/index.ts @@ -1,16 +1,7 @@ -import { - ActionReducerMap, - createFeatureSelector, - createSelector -} from '@ngrx/store'; -import { Store } from '@ngrx/store'; - -import * as fromActions from '../actions'; +import { ActionReducerMap } from '@ngrx/store'; import * as fromHeroes from './hero.reducer'; import * as fromVillains from './villain.reducer'; -export type Action = fromActions.HeroAction; - export interface EntityState { heroes: fromHeroes.HeroState; villains: fromVillains.VillainState; diff --git a/src/app/store/reducers/villain.reducer.ts b/src/app/store/reducers/villain.reducer.ts index f78792f..44059db 100644 --- a/src/app/store/reducers/villain.reducer.ts +++ b/src/app/store/reducers/villain.reducer.ts @@ -1,3 +1,4 @@ +import { Action, createReducer, on } from '@ngrx/store'; import { Villain } from '../../core'; import * as VillainActions from '../actions'; @@ -13,100 +14,6 @@ export const initialState: VillainState = { error: false }; -export function reducer( - state = initialState, - action: VillainActions.AllVillainActions -): VillainState { - switch (action.type) { - case VillainActions.ADD_VILLAIN: { - return { ...state, loading: true }; - } - - case VillainActions.ADD_VILLAIN_SUCCESS: { - return { - ...state, - loading: false, - villains: [...state.villains, { ...action.payload }] - }; - } - - case VillainActions.ADD_VILLAIN_ERROR: { - return { ...state, loading: false }; - } - - case VillainActions.GET_VILLAINS: { - return { ...state, loading: true }; - } - - case VillainActions.GET_VILLAINS_ERROR: { - return { - ...state, - loading: false - }; - } - - case VillainActions.GET_VILLAINS_SUCCESS: { - return { - ...state, - villains: action.payload, - loading: false - }; - } - - case VillainActions.DELETE_VILLAIN: { - return { - ...state, - loading: true, - villains: state.villains.filter(h => h !== action.payload) - }; - } - - case VillainActions.DELETE_VILLAIN_SUCCESS: { - const result = { ...state, loading: false }; - return result; - } - - case VillainActions.DELETE_VILLAIN_ERROR: { - return { - ...state, - villains: [...state.villains, action.payload.requestData], - loading: false - }; - } - - case VillainActions.UPDATE_VILLAIN: { - return { - ...state, - villains: state.villains.map(h => { - if (h.id === action.payload.id) { - state.loading = true; - } - return h; - }) - }; - } - - case VillainActions.UPDATE_VILLAIN_SUCCESS: { - return modifyVillainState(state, action.payload); - } - - case VillainActions.UPDATE_VILLAIN_ERROR: { - return { - ...state, - loading: false, - villains: state.villains.map(h => { - if (h.id === action.payload.requestData.id) { - // Huh? No idea what the error is! - state.error = true; - } - return h; - }) - }; - } - } - return state; -} - function modifyVillainState( villainState: VillainState, villainChanges: Partial @@ -114,12 +21,71 @@ function modifyVillainState( return { ...villainState, loading: false, - villains: villainState.villains.map(h => { - if (h.id === villainChanges.id) { - return { ...h, ...villainChanges }; + villains: villainState.villains.map(v => { + if (v.id === villainChanges.id) { + return { ...v, ...villainChanges }; } else { - return h; + return v; } }) }; } + +const villainReducer = createReducer( + initialState, + on(VillainActions.addVillain, state => ({ ...state, loading: true })), + on(VillainActions.addVillainSuccess, (state, { villain }) => ({ + ...state, + loading: false, + villains: [...state.villains, { ...villain }] + })), + on(VillainActions.addVillainError, state => ({ ...state, loading: false })), + on(VillainActions.getVillains, state => ({ ...state, loading: true })), + on(VillainActions.getVillainsError, state => ({ ...state, loading: false })), + on(VillainActions.getVillainsSuccess, (state, { villains }) => ({ + ...state, + loading: false, + villains + })), + on(VillainActions.deleteVillain, (state, { villain }) => ({ + ...state, + loading: false, + villains: state.villains.filter(v => v !== villain) + })), + on(VillainActions.deleteVillainSuccess, state => ({ + ...state, + loading: false + })), + on(VillainActions.deleteVillainError, (state, { error }) => ({ + ...state, + villains: [...state.villains, error.requestData], + loading: false + })), + on(VillainActions.updateVillain, (state, { villain }) => ({ + ...state, + villains: state.villains.map(v => { + if (v.id === villain.id) { + state.loading = true; + } + return v; + }) + })), + on(VillainActions.updateVillainSuccess, (state, { villain }) => + modifyVillainState(state, villain) + ), + on(VillainActions.updateVillainError, (state, { error }) => ({ + ...state, + villains: state.villains.map(v => { + if (v.id === error.requestData.id) { + // Huh? No idea what the error is! + state.error = true; + } + return v; + }), + loading: false + })) +); + +export function reducer(state: VillainState | undefined, action: Action) { + return villainReducer(state, action); +} diff --git a/src/app/store/services/hero-http-dispatchers.service.ts b/src/app/store/services/hero-http-dispatchers.service.ts index acbb73b..db3a3e7 100644 --- a/src/app/store/services/hero-http-dispatchers.service.ts +++ b/src/app/store/services/hero-http-dispatchers.service.ts @@ -16,8 +16,8 @@ export class HeroHttpDispatchers { this.heroDataService .getHeroes() .subscribe( - heroes => this.dispatch(new HeroActions.GetHeroesSuccess(heroes)), - error => this.dispatch(new HeroActions.GetHeroesError(error)) + heroes => this.dispatch(HeroActions.getHeroesSuccess({ heroes })), + error => this.dispatch(HeroActions.getHeroesError(error)) ); } @@ -25,19 +25,21 @@ export class HeroHttpDispatchers { this.dispatchLoading(); this.heroDataService.addHero(hero).subscribe( // pessimistic add: add hero to cache only when the server responds with success - addedHero => this.dispatch(new HeroActions.AddHeroSuccess(addedHero)), - error => this.dispatch(new HeroActions.AddHeroError(error)) + addedHero => + this.dispatch(HeroActions.addHeroSuccess({ hero: addedHero })), + error => this.dispatch(HeroActions.addHeroError(error)) ); } deleteHero(hero: Hero) { this.dispatchLoading(); // optimistic delete: delete hero immediately from cache, before making request - this.dispatch(new HeroActions.DeleteHero(hero)); + this.dispatch(HeroActions.deleteHero({ hero })); this.heroDataService.deleteHero(hero).subscribe( - addedHero => this.dispatch(new HeroActions.DeleteHeroSuccess(addedHero)), + addedHero => + this.dispatch(HeroActions.deleteHeroSuccess({ hero: addedHero })), // no recovery: don't bother restoring the hero to cache when server responds with error - error => this.dispatch(new HeroActions.DeleteHeroError(error)) + error => this.dispatch(HeroActions.deleteHeroError(error)) ); } @@ -45,8 +47,9 @@ export class HeroHttpDispatchers { this.dispatchLoading(); this.heroDataService.updateHero(hero).subscribe( // pessimistic update: update hero in cache only when the server responds with success - addedHero => this.dispatch(new HeroActions.UpdateHeroSuccess(addedHero)), - error => this.dispatch(new HeroActions.UpdateHeroError(error)) + addedHero => + this.dispatch(HeroActions.updateHeroSuccess({ hero: addedHero })), + error => this.dispatch(HeroActions.updateHeroError(error)) ); } @@ -56,5 +59,6 @@ export class HeroHttpDispatchers { ) {} private dispatch = (action: Action) => this.store.dispatch(action); - private dispatchLoading = () => this.dispatch(new HeroActions.SetHeroLoading(true)); + private dispatchLoading = () => + this.dispatch(HeroActions.setHeroLoading({ loading: true })); } diff --git a/src/app/store/services/hero.dispatchers.ts b/src/app/store/services/hero.dispatchers.ts index 7cb2e48..c3a9ea7 100644 --- a/src/app/store/services/hero.dispatchers.ts +++ b/src/app/store/services/hero.dispatchers.ts @@ -1,27 +1,26 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; - -import { EntityState } from '../reducers'; import { Hero } from '../../core'; import * as HeroAction from '../actions'; +import { EntityState } from '../reducers'; @Injectable() export class HeroDispatchers { constructor(private store: Store) {} deleteHero(hero: Hero) { - this.store.dispatch(new HeroAction.DeleteHero(hero)); + this.store.dispatch(HeroAction.deleteHero({ hero })); } addHero(hero: Hero) { - this.store.dispatch(new HeroAction.AddHero(hero)); + this.store.dispatch(HeroAction.addHero({ hero })); } updateHero(hero: Hero) { - this.store.dispatch(new HeroAction.UpdateHero(hero)); + this.store.dispatch(HeroAction.updateHero({ hero })); } getHeroes() { - this.store.dispatch(new HeroAction.GetHeroes()); + this.store.dispatch(HeroAction.getHeroes()); } } diff --git a/src/app/store/services/villain.dispatchers.ts b/src/app/store/services/villain.dispatchers.ts index 2b65415..7f0725c 100644 --- a/src/app/store/services/villain.dispatchers.ts +++ b/src/app/store/services/villain.dispatchers.ts @@ -1,27 +1,26 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; - -import { EntityState } from '../reducers'; import { Villain } from '../../core'; import * as VillainAction from '../actions'; +import { EntityState } from '../reducers'; @Injectable() export class VillainDispatchers { constructor(private store: Store) {} deleteVillain(villain: Villain) { - this.store.dispatch(new VillainAction.DeleteVillain(villain)); + this.store.dispatch(VillainAction.deleteVillain({ villain })); } addVillain(villain: Villain) { - this.store.dispatch(new VillainAction.AddVillain(villain)); + this.store.dispatch(VillainAction.addVillain({ villain })); } updateVillain(villain: Villain) { - this.store.dispatch(new VillainAction.UpdateVillain(villain)); + this.store.dispatch(VillainAction.updateVillain({ villain })); } getVillains() { - this.store.dispatch(new VillainAction.GetVillains()); + this.store.dispatch(VillainAction.getVillains()); } }