Как получить текущее значение объекта состояния с помощью @ngrx / store?
Мой класс обслуживания, прежде чем вызывать веб-службу, должен получить свойство с именем 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-магазине-например, (https://github.com/ngrx/angular2-store-example/blob/master/src/app/users/models/users.ts) в качестве руководства при выполнении.
Интересно, существует ли лучший (более чистый) способ?
Живой пример: https://plnkr.co/edit/WRPfMzPolQsYNGzBUS1g?p=preview
8 ответов:
Оригинальный ответ для @ngrx / store v1.x
@ngrx/storeпростирается BehaviorSubject иvalueсвойство можно использовать.this._store.valueЭто будет текущее состояние вашего приложения,и оттуда вы можете выбрать Свойства, фильтр, карту и т. д...
Обновление:
Мне потребовалось некоторое время, чтобы понять, что есть что в вашем примере (: чтобы получить текущее значение
dataForUpdate, Вы можете использовать:let x = this._store.value.StateReducer.dataForUpdate; console.log(x); // => { key: "123" }Обновление для @ngrx / store v2.x
С обновлением до версии 2,
valueбыл удален, как описано в docs:API для синхронного извлечения из хранилища последнего значения состояния были удалены. Вместо этого вы всегда можете полагаться на
subscribe(), работающий синхронно, если вам нужно получить значение состояния:function getState(store: Store<State>): State { let state: State; store.take(1).subscribe(s => state = s); return state; }
withLatestFrom()илиcombineLatest()методы в цепочке подписки дают вам именно то, что вам нужно, и согласуются с духом Observables+Ngrx.Вместо состояния GET
.mergeMap()в приведенном выше коде использованиеwithLatestFrom()будет выглядеть примерно так:В качестве отступления, код в исходном вопросе, по-видимому, управляет асинхронными эффектами действий redux, для чего и предназначена библиотека ngrx/effects. Я предлагаю вам проверить это. После того, как вы получите эффекты, связанные проволокой, код для управления асинхронными действиями redux намного чище. Эта статья Джима Линча была также очень полезна для меня: основы "ngrx / effects", @Effect и Async Middleware для "ngrx / store" в Angular 2... .withLatestFrom(store$, (payload, state) => { return {payload: payload, stateData: state.data} } ) ...
Не совсем прямой ответ на этот вопрос, но я нашел эту страницу, ища, как получить одно значение из магазина.
Чтобы достичь этого, вы можете ввести объект
Stateиз@ngrx/store, как показано ниже:import { State } from '@ngrx/store'; constructor (private state: State<AppState>) { let propertyValue = state.getValue().path.to.state.property; }Объект
stateсодержит текущее состояние в частном свойстве_value, доступ к которому осуществляется с помощью метода.getValue().
Я создал минималистичное приложение, которое имеет состояние с 2 счетчиками, которые являются свойствами AppState, и 2 редукторами. Каждый редуктор привязан к определенному счетчику, и я подписался на наблюдаемый для каждого счетчика, который будет
Существует кнопка, которая вызывает оба редуктора, посылая событие. Кроме того, 2 счетчика привязаны к 2 меткам, поэтому изменения в них показывают -console.logего значение. Сами редукторы также пишут на консоль при вызове.<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 { }
После ответа от @Sasxa синтаксис изменился на более новых версиях
@nrgx/store(v5 и v6). После обновления базовой библиотеки RxJS до версии ^5.5.0 на всех экземплярахObservableтеперь доступен метод pipe, позволяющий упростить цепочку и изменить способ получения подписки.Итак, теперь вы можете сделать что-то вроде:
import { take } from 'rxjs/operators'; store.select('your-state').pipe(take(1)).subscribe( val => console.log(val) );Или, используя строго оператор
pipe():import { select } from '@ngrx/store'; import { take } from 'rxjs/operators'; store.pipe(select('your-state'), take(1)).subscribe( val => console.log(val) );
Мое Решение
Класс State в ngStore является объектом 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); }
Дополнительный комментарий. Когда я использую this._store.value.StateReducer.currentPeriod.id
Transpiler возвращение "приложение/государственной/государственной службы.ts (133,35): ошибка TS2339: свойство 'StateReducer' не существует для типа 'AppState'."
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 }; }) ; }Чтобы исправить проблему, я изменил BehaviorSubject.d. ts в папке rxjs\subject:
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; }Не уверен, что это законная модификация ;)
Это работа для меня. я получу свои объектные данные.
this.store.select('dataStore').subscribe(data => { console.log(data) }
Comments