Avant d'appeler un service Web, ma classe de service doit obtenir une propriété appelée dataForUpdate
de mon état. Actuellement, je le fais comme ça:
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));
}
J'utilise angular2-store-example ( https://github.com/ngrx/angular2-store-example/blob/master/src/app/users/models/users.ts ) comme guide pour suivre.
Je me demande s'il existe une meilleure méthode (plus propre)?
Exemple en direct: https://plnkr.co/edit/WRPfMzPolQsYNGzBUS1g?p=preview
@ngrx/store
extend BehaviorSubject et il possède une propriété value
que vous pouvez utiliser.
this._store.value
ce sera l'état actuel de votre application, et à partir de là, vous pourrez sélectionner des propriétés, filtrer, mapper, etc.
mettre à jour:
Il m'a fallu un certain temps pour comprendre ce que votre exemple donne (: Pour obtenir la valeur actuelle de dataForUpdate
, vous pouvez utiliser:
let x = this._store.value.StateReducer.dataForUpdate;
console.log(x); // => { key: "123" }
Avec la mise à jour vers la version 2, value
a été supprimé comme décrit dans docs :
Les API permettant d'extraire de manière synchrone la valeur d'état la plus récente de Store ont été supprimées. Au lieu de cela, vous pouvez toujours compter sur
subscribe()
s'exécutant de manière synchrone si vous devez obtenir la valeur d'état:
function getState(store: Store<State>): State {
let state: State;
store.take(1).subscribe(s => state = s);
return state;
}
Les méthodes withLatestFrom()
ou combineLatest()
de la chaîne d'abonnement vous donnent exactement ce dont vous avez besoin et sont alignées sur l'esprit d'Observables + Ngrx.
A la place de la fonction GET STATE .mergeMap()
dans le code ci-dessus, utiliser withLatestFrom()
ressemblerait à ceci:
...
.withLatestFrom(store$, (payload, state) => {
return {payload: payload, stateData: state.data}
} )
...
En passant, le code de la question initiale semble gérer les effets asynchrones des actions de redux, ce à quoi sert exactement la bibliothèque ngrx/effects . Je vous suggère de vérifier. Une fois les effets connectés, le code permettant de gérer les actions de redux asynchrones est beaucoup plus propre. Cet article de Jim Lynch m'a aussi beaucoup aidé: Les bases de "ngrx/effects", @Effect et Async Middleware pour "ngrx/store" dans Angular 2
Ce n’est pas strictement une réponse directe à la question, mais j’ai trouvé cette page cherchant comment récupérer une valeur unique du magasin.
Pour ce faire, vous pouvez injecter l'objet State
à partir de @ngrx/store
comme indiqué ci-dessous:
import { State } from '@ngrx/store';
constructor (private state: State<AppState>) {
let propertyValue = state.getValue().path.to.state.property;
}
L'objet state
contient l'état actuel dans une propriété privée _value
, accessible par la méthode .getValue()
.
Suite à la réponse de @Sasxa, la syntaxe a été modifiée pour les versions plus récentes de @nrgx/store
(v5 et v6). Une fois que la bibliothèque RxJS sous-jacente a été mise à jour à ^ 5.5.0, une méthode de canal est désormais disponible sur toutes les instances Observable
, ce qui facilite le chaînage et modifie la manière dont un abonnement est réalisé.
Donc, vous pouvez maintenant faire quelque chose comme:
import { take } from 'rxjs/operators';
store.select('your-state').pipe(take(1)).subscribe(
val => console.log(val)
);
Ou, en utilisant strictement l'opérateur pipe()
:
import { select } from '@ngrx/store';
import { take } from 'rxjs/operators';
store.pipe(select('your-state'), take(1)).subscribe(
val => console.log(val)
);
J'ai créé une application minimaliste qui a un état avec 2 compteurs qui sont des propriétés de l'AppState et 2 réducteurs. Chaque réducteur est lié à un compteur particulier et j'ai souscrit un observable pour chaque compteur qui console.log
sa valeur. Les réducteurs eux-mêmes écrivent également sur la console lorsqu'ils sont appelés.
Il existe un bouton qui appelle les deux réducteurs en envoyant un événement. De plus, les 2 compteurs sont liés à 2 étiquettes, leur modification indique donc - <p>Counter: {{counter1 | async}}</p>
.
Le mappage de chaque compteur sur un réducteur se fait avec 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 { }
la classe State dans ngStore est un BehaviorSubject, nous pouvons donc l'injecter et utiliser sa propriété value pour obtenir la dernière valeur.
constructor(private state:State<YourState>...) {
}
someMethod() {
// WHAT'S MORE: you can use your selector directly on it!
let v = yourSelector(this.state.value);
}