【发布时间】:2022-01-27 04:11:21
【问题描述】:
我遇到了一种情况,我需要从 2 个独立的数据源中获取数据。一个数据源依赖于来自存储中其他值的 2 个值。
在下面的代码示例中,我需要获取“银行”列表并呈现它们。然后在将来的某个时候,我需要“登录”并获取这些银行的“余额”列表。
我在依赖部分遇到问题。我可以手动订阅 observables 并且它可以工作,但我认为这不是正确的方法。我怀疑正确的方法是 rxjs 运算符和选择器的某种组合。我会很感激一些方向。
组件
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { getBalancesAction, getBanksAction, loginAction } from './store/app.actions';
import { selectBalances, selectBanks, selectLogin } from './store/app.selectors';
import { IAppState } from './store/app.state';
import * as _ from 'lodash';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit
{
// Desired behavior
// Banks render right off the bat.
// CLicking login
public banks$ = this.store.select(selectBanks);
public login$ = this.store.select(selectLogin);
public balances$ = this.store.select(selectBalances);
constructor(private store: Store<IAppState>)
{
}
ngOnInit()
{
this.store.dispatch(getBanksAction());
// You can view the list of banks without being logged in
// I need to call this.store.dispatch(getBalances(banks$, login$))
// but only after I have a login and the list of banks.
// I could subscribe, but that doesn't seem to be in the spirit of ngrx and rxjs.
this.banks$.subscribe(banks =>
{
if (banks)
{
this.login$.subscribe(login =>
{
if (login)
{
// getting balances requires the accountid and a list of banks. These come from observables of their own.
this.store.dispatch(getBalancesAction({ accountId: login, banks: _.map(banks, b => b.id) }))
}
})
}
});
}
public login(): void
{
this.store.dispatch(loginAction({ userName: 'justme' }));
}
}
动作
import { createAction, props } from "@ngrx/store";
import { IBalance, IBank } from "./app.state";
export enum BankActions
{
GET_BANKS = "GET_BANKS",
GET_BANKS_SUCCESS = "GET_BANKS_SUCCESS",
GET_BALANCES = "GET_BALANCES",
GET_BALANCE_SUCCESS = "GET_BALANCES_SUCCESS",
LOGIN = "LOGIN",
LOGIN_SUCCESS = "LOGIN_SUCCESS"
}
export const getBanksAction = createAction(
BankActions.GET_BANKS
);
export const getBanksSuccessAction = createAction(BankActions.GET_BANKS_SUCCESS, props<{ banks: IBank[] }>());
export const getBalancesAction = createAction(
BankActions.GET_BALANCES,
props<{ accountId: string, banks: string[] }>()
);
export const getBalancesSuccessAction = createAction(BankActions.GET_BALANCE_SUCCESS, props<{ balances: IBalance[] }>());
export const loginAction = createAction(
BankActions.LOGIN,
props<{userName: string}>()
)
export const loginSuccessAction= createAction(
BankActions.LOGIN_SUCCESS,
props<{accountId: string}>()
)
效果
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { defer, forkJoin, from, of } from 'rxjs';
import { map, mergeMap, tap } from 'rxjs/operators';
import { BanksService } from '../banks.service';
import { BankActions, getBalancesSuccessAction, getBanksSuccessAction, loginSuccessAction } from './app.actions';
@Injectable()
export class BankEffects
{
constructor(private actions$: Actions, private banksService: BanksService) { }
loadBalances$ = createEffect(() => this.actions$.pipe(
ofType(BankActions.GET_BALANCES),
mergeMap((x:any) => this.banksService.getBalances(x.accountId, x.banks)
.pipe(
map(b => getBalancesSuccessAction({ balances: b }))
))
)
);
loadBanks$ = createEffect(() => this.actions$.pipe(
ofType(BankActions.GET_BANKS),
mergeMap(() => this.banksService.getBanks()
.pipe(
map(b => getBanksSuccessAction({ banks: b }))
))
)
);
doLogin$ = createEffect(() => this.actions$.pipe(
ofType(BankActions.LOGIN),
mergeMap((x) => this.banksService.login(x)
.pipe(
map(b => loginSuccessAction({ accountId: b }))
))
)
);
}
减速器
import { createReducer, on } from "@ngrx/store";
import { getBalancesSuccessAction, loginSuccessAction, getBanksSuccessAction } from "./app.actions";
import { IAppState, initialState } from "./app.state";
export const appReducer = createReducer(
initialState,
on(getBalancesSuccessAction, (state: IAppState, { balances }) => ({ ...state, balances: balances })),
on(loginSuccessAction, (state: IAppState, { accountId }) => ({ ...state, accountId: accountId })),
on(getBanksSuccessAction, (state: IAppState, { banks }) =>
{
return ({ ...state, banks: banks });
}));
选择器
import { createSelector } from "@ngrx/store";
import { IAppState } from "./app.state";
export const selectFeature = (state: any) => state.app;
export const selectLogin = createSelector(
selectFeature,
(state: IAppState) => state.accountId
);
export const selectBanks = createSelector(
selectFeature,
(state: IAppState) =>
{
return state.banks;
}
);
export const selectBalances = createSelector(
selectFeature,
(state: IAppState) => state.balances
);
状态
export interface IBank
{
id: string;
name: string;
}
export interface IBalance{
bankId: string,
value: number;
internalAccountNumber: string
}
export interface IAppState
{
banks: IBank[],
accountId: string | null,
balances: IBalance[]
}
export const initialState: IAppState = {
banks: [],
accountId: null,
balances: []
};
【问题讨论】:
标签: angular ngrx ngrx-store