Как получить текущее значение объекта состояния с помощью @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-store-пример (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()
будет выглядеть примерно так:
...
.withLatestFrom(store$, (payload, state) => {
return {payload: payload, stateData: state.data}
} )
...
в стороне, код в исходном вопросе, по-видимому, управляет асинхронными эффектами действий redux, что именно то, что ngrx/эффект библиотека для. Я предлагаю вам проверить это из. После подключения эффектов код для управления асинхронными действиями redux становится намного чище. Эта статья Джима Линча также была очень полезна для меня:основы" ngrx/effects", @Effect и асинхронного промежуточного ПО для "ngrx / store" в 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()
метод.
Я создал минималистичное приложение, которое имеет состояние с 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 { }
после ответа от @Sasxa синтаксис изменился на более новых версиях @nrgx/store
(v5 и v6). После того, как базовая библиотека RxJS была обновлена до ^5.5.0, теперь на всех Observable
экземпляры, что позволяет упростить цепочку и изменяет способ достижения подписки.
так что теперь вы можете сделать что-то вроде:
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)
);
Мое Решение
класс состояния в 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);
}
дополнительные комментарии. Когда я использую это.Магазин.ценность.StateReducer.currentPeriod.id
Транспилер возврат " app / state / stateService.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) }