第3部あなたのPOKのDEXをビルドします.
76129 ワード
この投稿は、初心者から忍者までNGRXを使ってあなたのPOK . DEXを構築する方法について説明しています. Part 1. Build your Pokédex: Introduction to NGRX Part 2. Build your Pokédex: @ngrx/entity Part 3. Build your Pokédex: Improve NgRX using create* functions 第4部あなたのPOKのDEXをビルドします 第5部ビルドあなたのPOK
導入
アクションタイプ.は、アクションを識別するために使用される有名な文字列です.
小道具アクションメタデータです.例えばペイロード.
両方を比較するために、以下のコードは、私たちのpok . dexで新しいcreateaction関数を使用する方法を示します.
以前
コアチームはENUMの使用が必要でないと述べますが、私の特定のコーディングスタイルでは、アクションセットを意識するためにアクションenumを定義するのを好みます.
したがって、前後
CreateActionは、ActionCreatorと呼ばれる関数を返すファクトリ関数です.呼び出されたときにアクションオブジェクトを返します.したがって、アクションを送信するときにActionCreatorを呼び出す必要があります.
このため、アクションが作成されるすべてのエフェクトには、次のリファクタリングを適用する必要があります.
以前
創造的効果
@ ngrx/entityを使用することで非常に反復的であるので、状態の作成を自動化します. 自動化効果、アクションの作成と縮小機能を簡素化を使用して このシリーズの次の投稿は、次のような興味深いトピックをカバーします. ファサードパターンは、 アプリケーションの状態のテスト. このポストの最も重要な部分は、示される概念です、そして、テクニックまたは図書館は使われませんでした.したがって、このポストは、大きな角度のアプリケーションを開始し、建築の原則を適用する必要がある人のためのガイドとして撮影する必要があります.
Announing NgRx 8 Angular Architecture Best Practices Angular Architecture - ng-conf Angular Architecture (official docs) NGRX RxJS Facade Pattern このポストのGithub支店はhttps://github.com/Caballerog/ngrx-pokedex/tree/ngrx-part3
導入
このポストでは、我々はAngular フレームワークNgRX 国家管理図書館として.NGRX 8でリリースされた新しいCreate *関数を使用します.
中間レベルで角度を管理する方法を知っていて、適切にこのポストを理解するために州管理図書館が何であるかについてわかっていてください.このシリーズでは、具体的な例がどのように開発されているかを示します.
まず、これらのポストに沿って構築される結果を以下のGIFに示す.
を読むことが不可欠ですfirst and second このシリーズの部分は何が構築されている理解できるようにする.このポストでは、以前に開発されたコードを改善します@ngrx/entity
パッケージは、アクション、縮小、効果を作成するために必要なボイラープレートコードを簡素化します.
創造的行動
NGRXでは、アクションを作成するときに大量のボイラープレートコードが必要です.あなたは頻繁にenums、アクションの種類、クラス、ユニオンの種類を作成する必要があります.この新しいバージョンでは、簡単な方法でアクションを作成することができます.
NGRXコアチームは、このゴールに達するために有名な工場機能設計パターンを使用しました.ファクトリ関数はcreateactionです.CreateAction関数は2つのパラメータを受け取ります.
NGRXでは、アクションを作成するときに大量のボイラープレートコードが必要です.あなたは頻繁にenums、アクションの種類、クラス、ユニオンの種類を作成する必要があります.この新しいバージョンでは、簡単な方法でアクションを作成することができます.
NGRXコアチームは、このゴールに達するために有名な工場機能設計パターンを使用しました.ファクトリ関数はcreateactionです.CreateAction関数は2つのパラメータを受け取ります.
アクションタイプ.は、アクションを識別するために使用される有名な文字列です.
小道具アクションメタデータです.例えばペイロード.
以前
export class LoadPokemon implements Action {
readonly type = PokemonActionTypes.LOAD_POKEMONS;
constructor() {}
}
export class LoadPokemonSuccess implements Action {
readonly type = PokemonActionTypes.LOAD_POKEMONS_SUCCESS;
constructor(public payload: Array<Pokemon>) {}
}
アフターloadPokemonFailed = createAction(
PokemonActionTypes.LOAD_POKEMONS_FAILED,
props<{ message: string }>()
),
add: createAction(PokemonActionTypes.ADD, props<{ pokemon: Pokemon }>()),
前のコードでは、Action
インターフェイスtype
属性とpayload
コンストラクタの使用.一方、後のコードでは、createAction
関数は、最初のパラメータがtype
そして2番目のパラメータはprops
属性(コンテキストではペイロード).コアチームはENUMの使用が必要でないと述べますが、私の特定のコーディングスタイルでは、アクションセットを意識するためにアクションenumを定義するのを好みます.
したがって、前後
pokemon.action.ts
は以下の通りです.import { Action } from '@ngrx/store';
import { Pokemon } from '@models/pokemon.interface';
export enum PokemonActionTypes {
ADD = '[Pokemon] Add',
ADD_SUCCESS = '[Pokemon] Add success',
ADD_FAILED = '[Pokemon] Add failed',
LOAD_POKEMONS = '[Pokemon] Load pokemon',
LOAD_POKEMONS_SUCCESS = '[Pokemon] Load pokemon success',
LOAD_POKEMONS_FAILED = '[Pokemon] Load pokemon failed',
UPDATE = '[Pokemon] Update',
UPDATE_SUCCESS = '[Pokemon] Update success',
UPDATE_FAILED = '[Pokemon] Update failed',
DELETE = '[Pokemon] Delete',
DELETE_SUCCESS = '[Pokemon] Delete success',
DELETE_FAILED = '[Pokemon] Delete failed'
}
export class LoadPokemon implements Action {
readonly type = PokemonActionTypes.LOAD_POKEMONS;
constructor() {}
}
export class LoadPokemonSuccess implements Action {
readonly type = PokemonActionTypes.LOAD_POKEMONS_SUCCESS;
constructor(public payload: Array<Pokemon>) {}
}
export class LoadPokemonFailed implements Action {
readonly type = PokemonActionTypes.LOAD_POKEMONS_FAILED;
constructor(public message: string) {}
}
export class Add implements Action {
readonly type = PokemonActionTypes.ADD;
constructor(public pokemon: Pokemon) {}
}
export class AddSuccess implements Action {
readonly type = PokemonActionTypes.ADD_SUCCESS;
constructor(public pokemon: Pokemon) {}
}
export class AddFailed implements Action {
readonly type = PokemonActionTypes.ADD_FAILED;
constructor(public message: string) {}
}
export class Delete implements Action {
readonly type = PokemonActionTypes.DELETE;
constructor(public id: number) {}
}
export class DeleteSuccess implements Action {
readonly type = PokemonActionTypes.DELETE_SUCCESS;
constructor(public id: number) {}
}
export class DeleteFailed implements Action {
readonly type = PokemonActionTypes.DELETE_FAILED;
constructor(public message: string) {}
}
export class Update implements Action {
readonly type = PokemonActionTypes.UPDATE;
constructor(public pokemon: Pokemon) {}
}
export class UpdateSuccess implements Action {
readonly type = PokemonActionTypes.UPDATE_SUCCESS;
constructor(public pokemon: Pokemon) {}
}
export class UpdateFailed implements Action {
readonly type = PokemonActionTypes.UPDATE_FAILED;
constructor(public message: string) {}
}
export type PokemonActions =
| LoadPokemonSuccess
| Add
| AddSuccess
| AddFailed
| Delete
| DeleteSuccess
| DeleteFailed
| Update
| UpdateSuccess
| UpdateFailed;
import { createAction, props } from '@ngrx/store';
import { Pokemon } from '@models/pokemon.interface';
export enum PokemonActionTypes {
ADD = '[Pokemon] Add',
ADD_SUCCESS = '[Pokemon] Add success',
ADD_FAILED = '[Pokemon] Add failed',
LOAD_POKEMONS = '[Pokemon] Load pokemon',
LOAD_POKEMONS_SUCCESS = '[Pokemon] Load pokemon success',
LOAD_POKEMONS_FAILED = '[Pokemon] Load pokemon failed',
UPDATE = '[Pokemon] Update',
UPDATE_SUCCESS = '[Pokemon] Update success',
UPDATE_FAILED = '[Pokemon] Update failed',
REMOVE = '[Pokemon] Delete',
REMOVE_SUCCESS = '[Pokemon] Delete success',
REMOVE_FAILED = '[Pokemon] Delete failed'
}
export const actions = {
loadPokemon: createAction(PokemonActionTypes.LOAD_POKEMONS),
loadPokemonSuccess: createAction(
PokemonActionTypes.LOAD_POKEMONS_SUCCESS,
props<{ pokemons: Pokemon[] }>()
),
loadPokemonFailed: createAction(
PokemonActionTypes.LOAD_POKEMONS_FAILED,
props<{ message: string }>()
),
add: createAction(PokemonActionTypes.ADD, props<{ pokemon: Pokemon }>()),
addSuccess: createAction(
PokemonActionTypes.ADD_SUCCESS,
props<{ pokemon: Pokemon }>()
),
addFailed: createAction(
PokemonActionTypes.ADD_FAILED,
props<{ message: string }>()
),
remove: createAction(PokemonActionTypes.REMOVE, props<{ id: number }>()),
removeSuccess: createAction(
PokemonActionTypes.REMOVE_SUCCESS,
props<{ id: number }>()
),
removeFailed: createAction(
PokemonActionTypes.REMOVE_FAILED,
props<{ message: string }>()
),
update: createAction(
PokemonActionTypes.UPDATE,
props<{ pokemon: Pokemon }>()
),
updateSuccess: createAction(
PokemonActionTypes.UPDATE_SUCCESS,
props<{ pokemon: Pokemon }>()
),
updateFailed: createAction(
PokemonActionTypes.UPDATE_FAILED,
props<{ message: string }>()
)
};
エクスポートしましたaction
CONSTとは、アクション名をキーとしてアクション自身を値として含む辞書です.CreateActionは、ActionCreatorと呼ばれる関数を返すファクトリ関数です.呼び出されたときにアクションオブジェクトを返します.したがって、アクションを送信するときにActionCreatorを呼び出す必要があります.
this.store.dispatch(addSuccess(pokemon: Pokemon));
アクションのクラスに関連付けられたオブジェクトを作成する必要はなくなりました.関数を直接呼び出すことができます.このため、アクションが作成されるすべてのエフェクトには、次のリファクタリングを適用する必要があります.
以前
@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.LOAD_POKEMONS),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => new PokemonActions.LoadPokemonSuccess(pokemons)),
catchError(message => of(new PokemonActions.LoadPokemonFailed(message)))
)
)
);
アフター@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message => of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
この効果は、次のセクションでCreateEffect関数を使用してリファクタリングされます.創造的効果
NGRX 8はcreateEffect
に代わる方法@Effect()
デコレータ.使用の主な利点createEffect
デコレータの代わりに、それはタイプセーフです.つまり、効果が返されない場合はObservable<Action>
コンパイルエラーをスローします.
次のコード片ではloadAllPokemon$
新築前後の効果createEffect
メソッド.移行は非常に簡単です.
以前
@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message => of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
アフター
loadAllPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message =>
of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
したがって、前後pokemon.effects.ts
は以下の通りです.
以前
import * as PokemonActions from '@states/pokemon/pokemon.actions';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { Observable, of } from 'rxjs';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { Pokemon } from '@shared/interfaces/pokemon.interface';
import { PokemonService } from '@services/pokemon.service';
@Injectable()
export class PokemonEffects {
constructor(
private actions$: Actions,
private pokemonService: PokemonService,
public snackBar: MatSnackBar
) {}
POKEMON_ACTIONS_SUCCESS = [
PokemonActions.PokemonActionTypes.ADD_SUCCESS,
PokemonActions.PokemonActionTypes.UPDATE_SUCCESS,
PokemonActions.PokemonActionTypes.DELETE_SUCCESS,
PokemonActions.PokemonActionTypes.LOAD_POKEMONS_SUCCESS
];
POKEMON_ACTIONS_FAILED = [
PokemonActions.PokemonActionTypes.ADD_FAILED,
PokemonActions.PokemonActionTypes.UPDATE_FAILED,
PokemonActions.PokemonActionTypes.DELETE_FAILED,
PokemonActions.PokemonActionTypes.LOAD_POKEMONS_FAILED
];
@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.LOAD_POKEMONS),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(response => new PokemonActions.LoadPokemonSuccess(response)),
catchError(error => of(new PokemonActions.LoadPokemonFailed(error)))
)
)
);
@Effect()
addPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.ADD),
switchMap((action: any) =>
this.pokemonService.add(action.pokemon).pipe(
map((pokemon: Pokemon) => new PokemonActions.AddSuccess(pokemon)),
catchError(error => of(new PokemonActions.AddFailed(error)))
)
)
);
@Effect()
deletePokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.DELETE),
switchMap(({ id }) =>
this.pokemonService.delete(id).pipe(
map(() => new PokemonActions.DeleteSuccess(id)),
catchError(error => of(new PokemonActions.DeleteFailed(error)))
)
)
);
@Effect()
updatePokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.UPDATE),
switchMap(({ pokemon }) =>
this.pokemonService.update(pokemon).pipe(
map(() => new PokemonActions.UpdateSuccess(pokemon)),
catchError(error => of(new PokemonActions.UpdateFailed(error)))
)
)
);
@Effect({ dispatch: false })
successNotification$ = this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_SUCCESS),
tap(() =>
this.snackBar.open('SUCCESS', 'Operation success', {
duration: 2000
})
)
);
@Effect({ dispatch: false })
failedNotification$ = this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_FAILED),
tap(() =>
this.snackBar.open('FAILED', 'Operation failed', {
duration: 2000
})
)
);
}
アフター
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { Pokemon } from '@shared/interfaces/pokemon.interface';
import { actions as PokemonActions } from '@states/pokemon/pokemon.actions';
import { PokemonService } from '@services/pokemon.service';
import { of } from 'rxjs';
@Injectable()
export class PokemonEffects {
constructor(
private actions$: Actions,
private pokemonService: PokemonService,
public snackBar: MatSnackBar
) {}
POKEMON_ACTIONS_SUCCESS = [
PokemonActions.addSuccess,
PokemonActions.updateSuccess,
PokemonActions.removeSuccess,
PokemonActions.loadPokemonSuccess
];
POKEMON_ACTIONS_FAILED = [
PokemonActions.addFailed,
PokemonActions.updateFailed,
PokemonActions.removeFailed,
PokemonActions.loadPokemonFailed
];
loadAllPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message =>
of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
addPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.add),
switchMap((action: any) =>
this.pokemonService.add(action.pokemon).pipe(
map((pokemon: Pokemon) => PokemonActions.addSuccess({ pokemon })),
catchError(message => of(PokemonActions.addFailed({ message })))
)
)
)
);
deletePokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.remove),
switchMap(({ id }) =>
this.pokemonService.delete(id).pipe(
map(() => PokemonActions.removeSuccess({ id })),
catchError(message => of(PokemonActions.removeFailed({ message })))
)
)
)
);
updatePokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.update),
switchMap(({ pokemon }) =>
this.pokemonService.update(pokemon).pipe(
map(() => PokemonActions.updateSuccess({ pokemon })),
catchError(message => of(PokemonActions.updateFailed(message)))
)
)
)
);
successNotification$ = createEffect(
() =>
this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_SUCCESS),
tap(() =>
this.snackBar.open('SUCCESS', 'Operation success', {
duration: 2000
})
)
),
{ dispatch: false }
);
failedNotification$ = createEffect(
() =>
this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_FAILED),
tap(() =>
this.snackBar.open('FAILED', 'Operation failed', {
duration: 2000
})
)
),
{ dispatch: false }
);
}
使用する前にdispatch: false
各エフェクトに渡されるパラメータはcreateEffect
メソッド.オプションを覚えておいてください{ dispatch: false }
新しいアクションを送信しない効果に対して使用されます.このオプションを追加すると、Observable<Action>
.
還元剤
新しいcreateReducer
メソッドを使用すると、switch
文.があるon
アクションの型を区別するメソッドです.もう一つの興味深い事実は、還元器の取り扱われていない行動のためにデフォルトのケースを扱う必要はありません.
したがって、前後pokemon.reducers.ts
は以下の通りです.
以前
import { PokemonActionTypes, PokemonActions } from './pokemon.actions';
import { PokemonState, pokemonAdapter } from './pokemon.adapter';
export function pokemonInitialState(): PokemonState {
return pokemonAdapter.getInitialState();
}
export function pokemonReducer(
state: PokemonState = pokemonInitialState(),
action: PokemonActions
): PokemonState {
switch (action.type) {
case PokemonActionTypes.LOAD_POKEMONS_SUCCESS:
return pokemonAdapter.addAll(action.payload, state);
case PokemonActionTypes.ADD_SUCCESS:
return pokemonAdapter.addOne(action.pokemon, state);
case PokemonActionTypes.DELETE_SUCCESS:
return pokemonAdapter.removeOne(action.id, state);
case PokemonActionTypes.UPDATE_SUCCESS:
const { id } = action.pokemon;
return pokemonAdapter.updateOne(
{
id,
changes: action.pokemon
},
state
);
default:
return state;
}
}
アフター
import { Action, createReducer, on } from '@ngrx/store';
import { PokemonState, pokemonAdapter } from './pokemon.adapter';
import { actions as PokemonActions } from './pokemon.actions';
export function pokemonInitialState(): PokemonState {
return pokemonAdapter.getInitialState();
}
const pokemonReducer = createReducer(
pokemonInitialState(),
on(PokemonActions.loadPokemonSuccess, (state, { pokemons }) =>
pokemonAdapter.addAll(pokemons, state)
),
on(PokemonActions.addSuccess, (state, { pokemon }) =>
pokemonAdapter.addOne(pokemon, state)
),
on(PokemonActions.removeSuccess, (state, { id }) =>
pokemonAdapter.removeOne(id, state)
),
on(PokemonActions.updateSuccess, (state, { pokemon }) =>
pokemonAdapter.updateOne({ id: pokemon.id, changes: pokemon }, state)
)
);
export function reducer(state: PokemonState | undefined, action: Action) {
return pokemonReducer(state, action);
}
注意createReducer
methodパラメータのリストを受け取ります:
最初のパラメータは初期状態で、2番目のパラメータはon
メソッド.にon
最初のパラメータは関連するアクションです.私の場合、私は行動を維持しましたenum
データ構造が好きだからです.もちろん、ENUMを使用せずに直接アクションをエクスポートできます.The second parameter of the on
メソッドは、state
and payload
を受信する.その後、我々は強力な使用することができますEntityAdapter
最も一般的な操作を行う.
結論
このポストでは、我々は我々のPOK@ngrx/entity
パッケージのcreate*
関数.CREATE *関数の使用は、アプリケーションの状態の管理に不要な複雑さを減少させます.さらに、アダプタは最も一般的な操作(CRUD)を実行するために使用されます.
したがって、この投稿では以下のトピックを取り上げました.
@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message => of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
loadAllPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message =>
of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
import * as PokemonActions from '@states/pokemon/pokemon.actions';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { Observable, of } from 'rxjs';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { Pokemon } from '@shared/interfaces/pokemon.interface';
import { PokemonService } from '@services/pokemon.service';
@Injectable()
export class PokemonEffects {
constructor(
private actions$: Actions,
private pokemonService: PokemonService,
public snackBar: MatSnackBar
) {}
POKEMON_ACTIONS_SUCCESS = [
PokemonActions.PokemonActionTypes.ADD_SUCCESS,
PokemonActions.PokemonActionTypes.UPDATE_SUCCESS,
PokemonActions.PokemonActionTypes.DELETE_SUCCESS,
PokemonActions.PokemonActionTypes.LOAD_POKEMONS_SUCCESS
];
POKEMON_ACTIONS_FAILED = [
PokemonActions.PokemonActionTypes.ADD_FAILED,
PokemonActions.PokemonActionTypes.UPDATE_FAILED,
PokemonActions.PokemonActionTypes.DELETE_FAILED,
PokemonActions.PokemonActionTypes.LOAD_POKEMONS_FAILED
];
@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.LOAD_POKEMONS),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(response => new PokemonActions.LoadPokemonSuccess(response)),
catchError(error => of(new PokemonActions.LoadPokemonFailed(error)))
)
)
);
@Effect()
addPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.ADD),
switchMap((action: any) =>
this.pokemonService.add(action.pokemon).pipe(
map((pokemon: Pokemon) => new PokemonActions.AddSuccess(pokemon)),
catchError(error => of(new PokemonActions.AddFailed(error)))
)
)
);
@Effect()
deletePokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.DELETE),
switchMap(({ id }) =>
this.pokemonService.delete(id).pipe(
map(() => new PokemonActions.DeleteSuccess(id)),
catchError(error => of(new PokemonActions.DeleteFailed(error)))
)
)
);
@Effect()
updatePokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.UPDATE),
switchMap(({ pokemon }) =>
this.pokemonService.update(pokemon).pipe(
map(() => new PokemonActions.UpdateSuccess(pokemon)),
catchError(error => of(new PokemonActions.UpdateFailed(error)))
)
)
);
@Effect({ dispatch: false })
successNotification$ = this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_SUCCESS),
tap(() =>
this.snackBar.open('SUCCESS', 'Operation success', {
duration: 2000
})
)
);
@Effect({ dispatch: false })
failedNotification$ = this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_FAILED),
tap(() =>
this.snackBar.open('FAILED', 'Operation failed', {
duration: 2000
})
)
);
}
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { Pokemon } from '@shared/interfaces/pokemon.interface';
import { actions as PokemonActions } from '@states/pokemon/pokemon.actions';
import { PokemonService } from '@services/pokemon.service';
import { of } from 'rxjs';
@Injectable()
export class PokemonEffects {
constructor(
private actions$: Actions,
private pokemonService: PokemonService,
public snackBar: MatSnackBar
) {}
POKEMON_ACTIONS_SUCCESS = [
PokemonActions.addSuccess,
PokemonActions.updateSuccess,
PokemonActions.removeSuccess,
PokemonActions.loadPokemonSuccess
];
POKEMON_ACTIONS_FAILED = [
PokemonActions.addFailed,
PokemonActions.updateFailed,
PokemonActions.removeFailed,
PokemonActions.loadPokemonFailed
];
loadAllPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message =>
of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
addPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.add),
switchMap((action: any) =>
this.pokemonService.add(action.pokemon).pipe(
map((pokemon: Pokemon) => PokemonActions.addSuccess({ pokemon })),
catchError(message => of(PokemonActions.addFailed({ message })))
)
)
)
);
deletePokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.remove),
switchMap(({ id }) =>
this.pokemonService.delete(id).pipe(
map(() => PokemonActions.removeSuccess({ id })),
catchError(message => of(PokemonActions.removeFailed({ message })))
)
)
)
);
updatePokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.update),
switchMap(({ pokemon }) =>
this.pokemonService.update(pokemon).pipe(
map(() => PokemonActions.updateSuccess({ pokemon })),
catchError(message => of(PokemonActions.updateFailed(message)))
)
)
)
);
successNotification$ = createEffect(
() =>
this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_SUCCESS),
tap(() =>
this.snackBar.open('SUCCESS', 'Operation success', {
duration: 2000
})
)
),
{ dispatch: false }
);
failedNotification$ = createEffect(
() =>
this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_FAILED),
tap(() =>
this.snackBar.open('FAILED', 'Operation failed', {
duration: 2000
})
)
),
{ dispatch: false }
);
}
新しい
createReducer
メソッドを使用すると、switch
文.があるon
アクションの型を区別するメソッドです.もう一つの興味深い事実は、還元器の取り扱われていない行動のためにデフォルトのケースを扱う必要はありません.したがって、前後
pokemon.reducers.ts
は以下の通りです.以前
import { PokemonActionTypes, PokemonActions } from './pokemon.actions';
import { PokemonState, pokemonAdapter } from './pokemon.adapter';
export function pokemonInitialState(): PokemonState {
return pokemonAdapter.getInitialState();
}
export function pokemonReducer(
state: PokemonState = pokemonInitialState(),
action: PokemonActions
): PokemonState {
switch (action.type) {
case PokemonActionTypes.LOAD_POKEMONS_SUCCESS:
return pokemonAdapter.addAll(action.payload, state);
case PokemonActionTypes.ADD_SUCCESS:
return pokemonAdapter.addOne(action.pokemon, state);
case PokemonActionTypes.DELETE_SUCCESS:
return pokemonAdapter.removeOne(action.id, state);
case PokemonActionTypes.UPDATE_SUCCESS:
const { id } = action.pokemon;
return pokemonAdapter.updateOne(
{
id,
changes: action.pokemon
},
state
);
default:
return state;
}
}
アフターimport { Action, createReducer, on } from '@ngrx/store';
import { PokemonState, pokemonAdapter } from './pokemon.adapter';
import { actions as PokemonActions } from './pokemon.actions';
export function pokemonInitialState(): PokemonState {
return pokemonAdapter.getInitialState();
}
const pokemonReducer = createReducer(
pokemonInitialState(),
on(PokemonActions.loadPokemonSuccess, (state, { pokemons }) =>
pokemonAdapter.addAll(pokemons, state)
),
on(PokemonActions.addSuccess, (state, { pokemon }) =>
pokemonAdapter.addOne(pokemon, state)
),
on(PokemonActions.removeSuccess, (state, { id }) =>
pokemonAdapter.removeOne(id, state)
),
on(PokemonActions.updateSuccess, (state, { pokemon }) =>
pokemonAdapter.updateOne({ id: pokemon.id, changes: pokemon }, state)
)
);
export function reducer(state: PokemonState | undefined, action: Action) {
return pokemonReducer(state, action);
}
注意createReducer
methodパラメータのリストを受け取ります:最初のパラメータは初期状態で、2番目のパラメータは
on
メソッド.にon
最初のパラメータは関連するアクションです.私の場合、私は行動を維持しましたenum
データ構造が好きだからです.もちろん、ENUMを使用せずに直接アクションをエクスポートできます.The second parameter of the on
メソッドは、state
and payload
を受信する.その後、我々は強力な使用することができますEntityAdapter
最も一般的な操作を行う.結論
このポストでは、我々は我々のPOK@ngrx/entity
パッケージのcreate*
関数.CREATE *関数の使用は、アプリケーションの状態の管理に不要な複雑さを減少させます.さらに、アダプタは最も一般的な操作(CRUD)を実行するために使用されます.
したがって、この投稿では以下のトピックを取り上げました.
@ngrx/entity
. @ngrx/data
パッケージ.より多くの.
Reference
この問題について(第3部あなたのPOKのDEXをビルドします.), 我々は、より多くの情報をここで見つけました https://dev.to/angular/part-3-build-your-pokedex-improve-ngrx-using-create-functions-2kj4テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol