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

628   8  

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/effects. Я предлагаю вам проверить это. После того, как вы получите эффекты, связанные проволокой, код для управления асинхронными действиями redux намного чище. Эта статья Джима Линча была также очень полезна для меня: основы "ngrx / effects", @Effect и Async Middleware для "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 теперь доступен метод 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

    Ничего не найдено.