我的服务类在调用 Web 服务之前,需要从我的状态获取一个名为
dataForUpdate
的属性。目前,我正在这样做:
constructor(public _store: Store < AppState > ,
public _APIService: APIService) {
const store$ = this._store.select('StateReducer');
.../...
let update = this.actions$.filter(action => action.type == UPDATE)
.do((action) => this._store.dispatch({
type: REDUCER_UPDATING,
payload: action.payload
})) **
* GET STATE ** *= => .mergeMap(action => store$.map((state: AppState) => state.dataForUpdate).distinctUntilChanged(),
(action, dataForUpdate) {
return {
type: action.type,
payload: {
employee: action.payload,
dataForUpdate: dataForUpdate
}
};
}) *
AND CALL API *= => .mergeMap(action => this._APIService.updateEmployee(action.payload.employee, action.payload.dataForUpdate),
(action, APIResult) => {
return {
type: REDUCER_UPDATED
}
})
.share();
.../...
let all = Observable.merge(update, ....);
all.subscribe((action: Action) => this._store.dispatch(action));
}
我使用 angular2-store-example (https://github.com/ngrx/angular2-store-example/blob/master/src/app/users/models/users.ts) 作为遵循的指南.
我想知道是否存在更好(更干净)的方法?
@ngrx/store
扩展了 BehaviorSubject 并且它有一个可以使用的 value
属性。
this._store.value
这将是您应用程序的当前状态,您可以从那里选择属性、过滤器、地图等...
更新:
我花了一段时间才弄清楚您的示例中的内容(:要获取
dataForUpdate
的当前值,您可以使用:
let x = this._store.value.StateReducer.dataForUpdate;
console.log(x); // => { key: "123" }
随着版本 2 的更新,
value
已被删除,如 docs: 中所述
删除了从 Store 同步拉取最新状态值的 API。相反,如果您必须获取状态值,您始终可以依赖
同步运行:subscribe()
function getState(store: Store<State>): State {
let state: State;
store.take(1).subscribe(s => state = s);
return state;
}
根据 @Sasxa 的回答,较新版本的
@nrgx/store
(v5 和 v6)的语法发生了变化。在底层 RxJS 库更新到 ^5.5.0 后,现在所有 Observable
实例上都可以使用管道方法,这允许更轻松的链接并改变订阅的实现方式。
所以你现在可以做类似的事情:
import { take } from 'rxjs/operators';
function getState(store: Store<State>): State {
let state: State;
store.select('your-state').pipe(take(1)).subscribe(
s => state = s
);
return state;
}
或者,严格使用
pipe()
运算符:
import { select } from '@ngrx/store';
import { take } from 'rxjs/operators';
function getState(store: Store<State>): State {
let state: State;
store.pipe(select('your-state'), take(1)).subscribe(
s => state = s
);
return state;
}
如果您想让代码更具可读性,您还可以采用异步/等待机制,如下所示:
import { select } from '@ngrx/store';
import { take } from 'rxjs/operators';
function async getStateAsync(store: Store<State>): State {
let state = await store
.pipe(
select('your-state'),
take(1)
)
.toPromise<State>();
return state;
}
withLatestFrom()
或 combineLatest()
方法可以满足您的需求,并且符合 Observables+Ngrx 的精神。
代替上面代码中的 GET STATE
.mergeMap()
,使用 withLatestFrom()
看起来像这样:
...
.withLatestFrom(store$, (payload, state) => {
return {payload: payload, stateData: state.data}
} )
...
顺便说一句,原始问题中的代码似乎是管理 redux 操作的异步效果,这正是 ngrx/effects 库的用途。我建议你检查一下。连接 Effects 后,用于管理异步 redux 操作的代码就干净多了。 Jim Lynch 的这篇文章对我也非常有帮助:The Basics of "ngrx/effects", @Effect, and Async Middleware for "ngrx/store" in Angular 2
严格来说不是问题的直接答案,但我发现此页面正在寻找如何从商店检索单个值。
要实现此目的,您可以从
State
注入 @ngrx/store
对象,如下所示:
import { State } from '@ngrx/store';
constructor (private state: State<AppState>) {
let propertyValue = state.getValue().path.to.state.property;
}
state
对象将当前状态保存在私有 _value
属性中,可通过 .getValue()
方法访问。
ngStore中的State类是一个BehaviorSubject,所以我们可以注入它,并使用它的value属性来获取最新的值。
constructor(private state:State<YourState>...) {
}
someMethod() {
// WHAT'S MORE: you can use your selector directly on it!
let v = yourSelector(this.state.value);
}
从 v4.x 开始,我们被迫将
take
运算符放入管道中以使其同步:
function getState(store: Store<State>): State {
let state: State;
store.pipe(take(1)).subscribe(s => state = s);
return state;
}
这对我有用。您需要从“@ngrx/store”导入 Store,AppState 是您的状态。
private state: AppState;
constructor(private store: Store<AppState>) { }
ngOnInit() {
this.store.select(x => this.state = x).subscribe();
}
额外评论。当我使用 this._store.value.StateReducer.currentPeriod.id
Transpiler 返回“app/state/stateService.ts(133,35):错误 TS2339:类型“AppState”上不存在属性“StateReducer”。”
constructor ( public _store: Store<AppState>) {
const store$ = this._store.select ('StateReducer');
.../...
let saveTransaction = this.actions$
.filter (action => action.type==SAVE_TRANSACTION )
.map (action => { return { type:SAVING_TRANSACTION, payload : action.payload }; } )
.mergeMap ( action => this._transactionService.updateTransaction (
this._store.value.StateReducer.currentProfile.id,
this._store.value.StateReducer.currentPeriod.id,
action.payload),
(state, webServiceResponse) => { return { type:TRANSACTION_UPDATED, payload :null }; }) ;
}
为了解决问题,我更改了 rxjs\subject 文件夹中的BehaviorSubject.d.ts :
import { Subject } from '../Subject';
import { Subscriber } from '../Subscriber';
import { Subscription } from '../Subscription';
export declare class BehaviorSubject<T> extends Subject<T> {
private _value;
private _hasError;
private _err;
constructor(_value: T);
getValue(): T;
value: T; <=== I have changed it to value: any;
_subscribe(subscriber: Subscriber<any>): Subscription<T>;
_next(value: T): void;
_error(err: any): void;
}
不确定这是否是合法的修改;)
我创建了一个简约的应用程序,它的状态有 2 个计数器(AppState 的属性)和 2 个减速器。每个减速器都绑定到一个特定的计数器,并且我为每个计数器订阅了一个可观察的值,它将
console.log
其值。减速器本身在调用时也会写入控制台。
有一个按钮可以通过分派事件来调用两个减速器。此外,2 个计数器绑定到 2 个标签,因此它们的变化显示 -
<p>Counter: {{counter1 | async}}</p>
。
将每个计数器映射到减速器是通过
StoreModule.forRoot({ counter1: Reducer1, counter2 : Reducer2 })
完成的
import { Component, NgModule } from '@angular/core';
import { Store, Action, StoreModule } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { BrowserModule } from '@angular/platform-browser';
interface AppState {
counter1 : number;
counter2 : number;
}
export function Reducer1(counter : number = 0, action : Action) {
console.log(`Called Reducer1: counter=${counter}`);
return counter + 1;
}
export function Reducer2(counter : number = 0, action : Action) {
console.log(`Called Reducer2: counter=${counter}`);
return counter + 2;
}
@Component({
selector: 'app-root',
template: `<p>Counter: {{counter1 | async}}</p>
<p>Counter: {{counter2 | async}}</p>
<button (click)='increment()'>Increment</button>`
})
export class AppComponent {
title = 'app';
counter1 : Observable<number>;
counter2 : Observable<number>;
constructor(private store : Store<AppState>) {
this.counter1 = this.store.select('counter1');
this.counter2 = this.store.select('counter2');
this.counter1.subscribe(x => console.log(`Subscribe event for counter1 fired: counter=${x}`));
this.counter2.subscribe(x => console.log(`Subscribe event for counter2 fired: counter=${x}`));
}
increment() {
this.store.dispatch({type:'foo'});
}
}
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
StoreModule.forRoot({ counter1: Reducer1, counter2 : Reducer2 })
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
这只是我对这个问题的经验,而不是标准
code
。
请看github上的答案: 状态快照#227
我想把
state
放入 constractor
,所以我习惯 unasynchronous
:
constructor (private state: State<AppState>) {
this.store.select("yourSelector").forEach(yourSelector => {
this.property = yourSelector.path.to.state.property
});
}
执行此操作的一种巧妙方法是将订阅包装在承诺中,然后等待承诺。
import { Store } from '@ngrx/store';
import { AppState } from './app.state';
// Assume some action and selector are defined in your NgRx application
// const someAction = ...;
// const someSelector = ...;
// Assume this function is defined in some common location.
export const getState = (store) => {
return new Promise((resolve, reject) => {
// Get an observable from the selector.
const someState$ = store.select(someSelector);
// Set up the subscription and resolve its callback value.
// This approach to defining the subscription lets you wire up
// both resolve and reject handling.
someState$.subscribe({
next: value => {
// Unsubscribe to prevent memory leaks
someState$.unsubscribe();
// Resolve the promise with the subscribed value
resolve(value);
},
error: err => {
// Unsubscribe and reject the promise in case of an error
someState$.unsubscribe();
reject(err);
}
});
});
}
// ...
// Assume this is called elsewhere, such as from your angular component,
// for example.
async doSomething() {
try {
const result = await getData(this.store);
console.log('Subscribed value:', result);
} catch (error) {
console.error('Error:', error);
}
}
这对我来说是工作。我会得到我的对象数据。
this.store.select('dataStore').subscribe(data => { console.log(data) }