Angular ngrx-store
Angularban a redux "megfelelője" az ngrx/store. Adatokat rakhatunk bele és olvashatjuk ki őket az alkalmazás bármelyik komponensében. |
|
Store-ban redux-ban olyan adatokat célszerű tárolni, amelyek nem változnak sűrűn, de viszont szükségünk lehet rájuk az alkalmazás több pontján pl profil adatok beállítások stb. Tegyük fel az @ngrx/store és @ngrx/effects csomagot. Majd az @ngrx/store-devtools csomagot. Ez utóbbi a chrome böngészőben levő redux-os devtool-os plugin-hoz szükséges, hogy megtudd nézni a böngészőben a store/state aktuális tartalmát. Ez a fejlesztésnél nagy segítség lehet. https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd Az angularban a redux-ot a következő részekből tudjuk megvalósítani: actions, effects, reduccers, services. Az action-ökben deklaráljuk a redux-ban használható hívások azonosítóit(action type-okat) és definiáljuk azokat a függvényeket, amiket meghívunk majd a komponensekből. Egy példa settings.actions.ts-re: import { Action } from '@ngrx/store'; export const SETTING_BEGIN = '[Setting] Load data begin'; export const SETTING_SUCCESS = '[Setting] Load data success'; export const SETTING_FAILURE = '[Setting] Load data failure'; export class LoadDataBegin implements Action { readonly type = SETTING_BEGIN; } export class LoadDataSuccess implements Action { readonly type = SETTING_SUCCESS; constructor(public payload: { data: any }) {} } export class LoadDataFailure implements Action { readonly type = SETTING_FAILURE; constructor(public payload: { error: any }) {} } /** * Export a type alias of all actions in this action group * so that reducers can easily compose action types */ export type SettingsActions = LoadDataBegin | LoadDataSuccess | LoadDataFailure; A reducer-ben kezeljük a fent definiált action-öket az action-type-okkal. Bekerülnek a redux/store state-be az adatok vagy onnan olvassuk ki őket és adjuk vissza az action-öknek. settings.reducer.ts példa fájl tartalma: import * as Actions from '../actions/settings.action'; export interface SettingsDataState { list: any; loading: boolean; error: any; } export const initialState: SettingsDataState = { list: {}, loading: false, error: null }; export function settingReducer(state = initialState, action: Actions.SettingsActions) { switch (action.type) { case Actions.SETTING_BEGIN: { return { ...state, loading: true, error: null }; } case Actions.SETTING_SUCCESS: { return { ...state, loading: false, list: action.payload.data }; } case Actions.SETTING_FAILURE: { return { ...state, loading: false, error: action.payload.error }; } default: { return state; } } } export const getSettings = (state: SettingsDataState) => state.list; Az effect "érzékeli", hogy melyik action type-ot választottuk ki és meghívja a megfelelő service-t és actionöket. Az settings.effect.ts fájl tartalma: import {Injectable} from '@angular/core'; import {Effect, Actions, ofType} from '@ngrx/effects'; import * as SettingsActions from '../actions/settings.action'; import {SettingsService} from "../services/settings.service"; import {map, switchMap, catchError} from 'rxjs/operators'; import { of } from 'rxjs'; @Injectable() export class SettingsEffect { constructor(private actions: Actions, private dataService: SettingsService) { } @Effect() loadData = this.actions.pipe( ofType(SettingsActions.SETTING_BEGIN), switchMap(() => { return this.dataService.getSettingListData().pipe( map(data => new SettingsActions.LoadDataSuccess({data: data})), catchError(error => of(new SettingsActions.LoadDataFailure({error: error.error})) ) ); }) ); } Service példa fájl, amit az effect hív meg és a reducceren keresztül bekerülnek az adatok a store-ba/redux-ba. import {map} from 'rxjs/operators'; import {Injectable} from '@angular/core'; import {Setting} from '../models/settings.model'; import {HttpClient} from '@angular/common/http'; import {Base64} from 'js-base64'; import {of, Observable} from 'rxjs'; import {AppState, getSelectLocationDataState} from "../_config/reducers"; import {Store} from '@ngrx/store'; @Injectable() export class SettingsService { protected settings: Setting[]; location: any; constructor(private http: HttpClient, private store: Store<AppState>) { this.store.select(getSelectLocationDataState).subscribe(data => this.location = data); this.clear(); } getSettingListData(): Observable<Setting[]> { if (this.location && this.location.id) { return this.http.get('/api/settings/' + this.location.id).pipe(map((r) => { return <Setting[]>r; })); } else { return of(); } } ... } A komponensekben a this.store.dispatch -el tudjuk meghívni az action-öket és a this.store.select-el lehet lekérdezni a store/redux tartalmát. Ha az adatok a redux/store-ba való betöltődése után szeretnénk műveleltet végezni, akkor az actionSubject-et használhatjuk. Példa komponensre: import {Store, ActionsSubject} from '@ngrx/store'; import * as SettingAction from '../actions/settings.action'; import { AppState, getSettingsDataState } from "../../../_config/reducers"; ... settings: Settings[]; constructor(private actionsSubject$: ActionsSubject, private store: Store<AppState>) { this.store.dispatch(new SettingAction.LoadDataBegin()); this.store.select(getSettingsDataState).subscribe(data => this.settings = data); } ngOnInit() { this.actionsSubject = this.actionsSubject$.subscribe((action: Action) => { if (action.type === SettingAction.SETTING_SUCCESS) { ... } }); } ngOnDestroy() { if (this.actionsSubject) { this.actionsSubject.unsubscribe(); } } Én készítettem egy reducers és effects config fájlt, amibe felvehetem az összes reducer-t és effect-et. Ez jól fog jönni az app.module-be való importáláshoz. reducers.ts: import { ActionReducerMap, createSelector, MetaReducer} from "@ngrx/store"; import * as settingReducer from "../reducers/settings.reducer"; export interface AppState { settings: settingReducer.SettingsDataState; } export const reducers: ActionReducerMap<AppState> = { settings: settingReducer.settingReducer }; export const metaReducers: MetaReducer<AppState>[] = [clearState]; export const getSettingsDataState = (state: AppState) => state.settings; effect.ts: import {SettingsEffect} from "../effects/settings.effect"; export const effects: any[] = [SettingsEffect]; Az app.module.ts-be a következő módon kell beállítanunk a létrehozott fájljainkat: import {StoreModule} from '@ngrx/store'; import {EffectsModule} from '@ngrx/effects'; import {reducers, metaReducers} from "./_config/reducers"; import {effects} from "./_config/effects"; ... imports: [ ... StoreModule.forRoot(reducers, { runtimeChecks: { strictStateImmutability: true, strictActionImmutability: true, strictStateSerializability: true, strictActionSerializability: true, }, metaReducers }), EffectsModule.forRoot(effects), RouterModule.forRoot(appRoutes), Olvasnivaló https://ngrx.io/guide/store https://github.com/ngrx/example-app |
2020.07.11. |
Figyelem! Az itt olvasható leírások, nem teljesek és nem biztos, hogy pontosak. Nem
frissülnek folyamatosan, ezért nem mindegyik használható az aktuális verziójú rendszerekben. Mindenki saját
felelősségére használja az itt található ötleteket. Az esetleges károkért nem vállalunk felelősséget.