我对ngrx / rxjs世界有点新意,并试图找出如何使用ngrx以正确的方式在我的Angular应用程序中设置我的效果。
我有两种工作方案:
现在,我正在寻找的效果逻辑是:
如果在状态参数
entities: Item[]
中找不到所选项 - 并且状态参数initialLoaded===false
- 则在尝试按id(再次)选择之前运行操作LIST_LOAD。
或者简单地说:
如果
initialLoaded===false
,则在执行常规ITEM_SELECT之前运行LIST_LOAD。
我目前的ITEM_SELECT效果代码,让我们开始:
@Effect() selectItemEffect: Observable<Action> = this.actions$
.ofType(MyActions.ActionTypes.ITEM_SELECT)
.withLatestFrom(this.store$)
.map(([action, state]) => {
const id = (<MyActions.ItemSelectAction>action).id;
const selectedItem = state.myItems.entities.find(x => x.id === id);
if(selectedItem){
return new MyActions.SelectSuccessAction(selectedItem);
} else {
/*
if(!state.myItems.initialLoaded) {
// run MyActions.ListLoadAction()?
// should be a nicer rxjs method for these scenarios?
}
*/
return new MyActions.ItemSelectFailureAction(new Error('Item not found'));
}
})
答案 0 :(得分:0)
设置更简单的解决方案。设置3个不同的操作,2加载项目列表,另一个返回选定的项目。
如果找不到该项,只需触发另一个动作,该动作调用执行api调用的服务以通过其id获取该项。
但是,无论如何,我觉得你是以一种棘手的方式处理它,你可以用这种方式分离动作处理:
LOAD_ITEMS
LOAD_SUCCESS
,SELECT
让我们假设您有一个Item
课程来为您的某个项目建模并且item.id
是string
,您应该采取以下行动:
import { Action } from '@ngrx/store';
/** App Models **/
import { Item } from '../models/item.model';
export const LOAD = '[Item] Load';
export const LOAD_SUCCESS = '[Item] Load Success';
export const SELECT = '[Item] Select';
export class LoadAction implements Action {
readonly type = LOAD;
constructor() { }
}
export class LoadActionSuccess implements Action {
readonly type = LOAD_SUCCESS;
constructor(public payload: Item[]) { }
}
export class SelectAction implements Action {
readonly type = SELECT;
constructor(public payload: string) {}
}
export type All
= LoadAction
| LoadActionSuccess
| SelectAction;
然后,您需要一个效果来使用服务执行api调用以返回第一个项目列表:
import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';
/** rxjs **/
import {map} from 'rxjs/operators/map';
import {mergeMap} from 'rxjs/operators/mergeMap';
import {catchError} from 'rxjs/operators/catchError';
/** ngrx **/
import * as itemsActions from '../actions/items.actions';
/** App Services **/
import { ItemService } from '../services/item.service';
/** App Model **/
import {Item} from '../models/content.model';
@Injectable()
export class ItemsEffects {
@Effect() load$ = this.actions$
.ofType(itemsActions.LOAD)
.pipe(
mergeMap(() => {
return this.itemsService.getItemsFromApi()
.pipe(
map((items: Item[]) => {
return new itemsActions.LoadActionSuccess(items);
}),
catchError((error: Error) => {
// handle error here
})
);
})
)
;
constructor(private itemsService: ItemsService, private actions$: Actions) { }
}
然后你需要一个减速器来处理Success
和Select
:
/** ngrx **/
import {createFeatureSelector} from '@ngrx/store';
import {createSelector} from '@ngrx/store';
/** App Models **/
import { Item } from '../models/item.model';
/** ngrx **/
import * as itemsActions from '../actions/items.actions';
export type Action = itemsActions.All;
export interface ItemsState {
items: Item[];
selectedItem: Item;
}
export const initialState: ContentsState = {
items: [],
selectedItem: new Item()
};
export const selectItems = createFeatureSelector<ItemsState>('items');
export const getItems = createSelector(selectItems, (state: ItemsState) => {
return state.items;
});
export const getItemById = createSelector(selectItems, (state: ItemsState) => {
return state.selectedItem;
});
export function itemsReducer(state: ItemsState = initialState, action: Action): ItemsState {
switch (action.type) {
case itemsActions.LOAD_SUCCESS:
const loadedItems = action.payload.map(item => new Item(item));
return {
...state,
items: loadedItems,
selectedItem: state.selectedItem
};
case itemsActions.SELECT:
// Find one item containing id equals to the one we passed
const filteredItem = state.items.filter((item) => {
return item.id === action.payload
})[0];
return {
...state,
items: state.items,
selectedItem: filteredItem
};
default: {
return state;
}
}
}
当然,在您的组件上,您应该使用以下内容获取新值:
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
/** rxjs **/
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
/** ngrx **/
import {AppState} from '../../shared/app-state.interface';
import * as itemsActions from './actions/items.actions';
import {getItemById, getItems} from './reducers/items.reducer';
/** App Models **/
import { Item } from './models/item.model';
@Component({
selector: 'app-items',
templateUrl: './items.component.html'
})
export class ItemsComponent implements OnInit {
items$: Observable<Array<Item>>;
selectedItems$: Observable<Item>;
selectItemById$ = new Subject<string>();
constructor(private store: Store<AppState>) {
this.items$ = this.store.select(getItems);
this.selectedItems$ = this.store.select(getItemById);
this.selectItemById$
.switchMap(id => this.store.dispatch(new itemsActions.SelectAction(id)));
}
ngOnInit() {
this.store.dispatch(new itemsActions.LoadAction());
}
}
选择在您的模板中完成,例如:
<... (input)="selectItemById$.next(id)"/>
AppState
只是一个界面:
import {ItemsState} from './items/reducers/items.reducers';
export interface AppState {
items: ItemsState;
}
或以任何其他方式表达。