angular - switchmap - rxjs pipe
¿Cómo obtener el valor actual del objeto de estado con @ ngrx/store? (8)
Mi clase de servicio, antes de llamar a un servicio web, necesita obtener una propiedad llamada dataForUpdate
de mi estado. Actualmente, lo estoy haciendo así:
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));
}
Estoy usando angular2-store-example ( https://github.com/ngrx/angular2-store-example/blob/master/src/app/users/models/users.ts ) como una guía a seguir.
Me pregunto si existe una manera mejor (más limpia)?
Ejemplo en vivo: https://plnkr.co/edit/WRPfMzPolQsYNGzBUS1g?p=preview
Mi solución
La clase State en ngStore es un BehaviorSubject, por lo que podemos inyectarlo y usar su propiedad value para obtener el último valor.
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;
}
Respuesta original para @ ngrx / store v1.x
@ngrx/store
extiende BehaviorSubject y tiene una propiedad de value
que puede usar.
this._store.value
ese será el estado actual de su aplicación, y desde allí puede seleccionar propiedades, filtro, mapa, etc.
actualizar:
Me tomó un tiempo averiguar qué es qué en tu ejemplo (: para obtener el valor actual de dataForUpdate
, puedes usar:
let x = this._store.value.StateReducer.dataForUpdate;
console.log(x); // => { key: "123" }
Actualización para @ ngrx / store v2.x
Con la actualización a la versión 2, el value
se eliminó como se describe en los docs :
Se han eliminado las API para extraer sin sincronización el valor de estado más reciente de la Tienda. En su lugar, siempre puede confiar en que
subscribe()
ejecuta de forma síncrona si tiene que obtener el valor de estado:
function getState(store: Store<State>): State {
let state: State;
store.take(1).subscribe(s => state = s);
return state;
}
Comentario adicional. Cuando uso this._store.value.StateReducer.currentPeriod.id
Transpiler return "app / state / stateService.ts (133,35): error TS2339: la propiedad ''StateReducer'' no existe en el tipo ''AppState''."
this.store.select(''dataStore'').subscribe(data => {
console.log(data)
}
Para solucionar el problema, he cambiado BehaviorSubject.d.ts en la carpeta 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; }
No estoy seguro si es una modificación legítima;)
He creado una aplicación minimalista que tiene un estado con 2 contadores que son propiedades de AppState y 2 reductores. Cada reductor está vinculado a un contador en particular, y he suscrito un observable para cada contador que console.log
su valor. Los propios reductores también escriben en la consola cuando son llamados.
Hay un botón que llama a ambos reductores enviando un evento. Además, los 2 contadores están vinculados a 2 etiquetas, por lo que los cambios en ellos muestran - <p>Counter: {{counter1 | async}}</p>
<p>Counter: {{counter1 | async}}</p>
.
La asignación de cada contador a un reductor se realiza con 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 { }
No es estrictamente una respuesta directa a la pregunta, pero encontré esta página buscando cómo recuperar un solo valor de la tienda.
Para lograr esto, puede inyectar el objeto State
desde @ngrx/store
como se muestra a continuación:
import { State } from ''@ngrx/store'';
constructor (private state: State<AppState>) {
let propertyValue = state.getValue().path.to.state.property;
}
El objeto de state
mantiene el estado actual en una propiedad privada _value
, a la que se accede mediante el método .getValue()
.
Siguiendo la respuesta de @Sasxa, la sintaxis cambió en las versiones más recientes de @nrgx/store
(v5 y v6). Después de que la biblioteca RxJS subyacente se actualizó a ^ 5.5.0, ahora hay un método de canalización disponible en todas las instancias Observable
, lo que permite un encadenamiento más fácil y cambia la forma en que se logra una suscripción.
Así que ahora puedes hacer algo como:
constructor(private state:State<YourState>...) {
}
someMethod() {
// WHAT''S MORE: you can use your selector directly on it!
let v = yourSelector(this.state.value);
}
O, utilizando estrictamente el operador pipe()
:
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 }; }) ;
}
esto es trabajo para mi obtendré mis datos de objeto
this.store.select(''dataStore'').subscribe(data => { console.log(data) }
withLatestFrom()
o combineLatest()
en la cadena de suscripción le ofrecen exactamente lo que necesita y están alineados con el espíritu de Observables + Ngrx.
En lugar de GET STATE .mergeMap()
en el código anterior, usar withLatestFrom()
se vería así:
...
.withLatestFrom(store$, (payload, state) => {
return {payload: payload, stateData: state.data}
} )
...
Además, el código de la pregunta original parece estar gestionando los efectos asíncronos de las acciones de redux, que es exactamente para lo que ngrx/effects biblioteca ngrx/effects . Te sugiero que lo mires. Una vez que se hayan conectado los Efectos, el código para administrar las acciones de redux asíncrono es mucho más limpio. Este artículo de Jim Lynch también fue muy útil para mí: los conceptos básicos de "ngrx / effects", @Effect y Async Middleware para "ngrx / store" en Angular 2