Как получить текущее значение объекта состояния с помощью @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)
 }