web-dev-qa-db-fra.com

Comment composez-vous un AppState avec des réducteurs pour plusieurs ressources / composants?

J'essaie de comprendre comment combiner de nombreux états de ressources en différents états de composants et ce qui constitue un AppState. La plupart des guides/exemples ngrx ne gèrent qu'une ressource (par exemple, un livre) ou un état limité (par exemple, des livres et un livre sélectionné), mais je ne pense pas avoir rencontré quelque chose de plus complexe que cela.

Que faites-vous lorsque vous disposez d'une douzaine de ressources, avec différents états (liste, élément, termes de recherche, éléments de menu, filtres, etc.) dans plusieurs composants nécessitant différents états de ressources?

J'ai cherché et j'ai trouvé la structure suivante, mais je ne suis pas convaincu que c'est ce qui était prévu:

AppState & reducer
<- combine reducers
- Component states & reducers
<- combine reducers
-- Resource states & reducers

Vous devez combiner des réducteurs de ressources (par exemple bookReducer, booksReducer, bookSearchTitleReducer) en un réducteur pertinent pour un composant (par exemple bookSearchReducer), puis combiner tous les réducteurs de composants en un seul réducteur avec un AppState et utiliser le fournisseur de magasin avec lui dans votre AppModule.

Est-ce la voie à suivre ou existe-t-il une autre manière (appropriée) de le faire? Et si c'est un bon moyen de le faire, utiliserais-je Store ou Store dans un constructeur de composants?

[Éditer]

Ok, l'application ngrx-example-app gère plus de composants, je vois qu'elle crée uniquement des états au niveau du composant, pas au niveau des ressources, combine les états et les réducteurs respectifs et utilise l'objet d'état complet dans le constructeur du composant: 'store: Boutique'.

Je suppose que puisque c'est un exemple officiel, ce serait le moyen prévu de gérer l'état/les réducteurs.

8
Kesarion

[Éditer]

Le nouveau v4 ngrx est beaucoup plus simple à utiliser, a une meilleure documentation et un exemple d'application pour vous aider. Ce qui suit est principalement pertinent pour la v2 et ses bizarreries, qui ne sont plus un problème dans la v4.

[Obsolète]

Après beaucoup d'essais et d'erreurs, j'ai trouvé une bonne formule de travail. Je vais partager l'essentiel ici, peut-être que cela aidera quelqu'un.

Le guide sur la composition du réducteur m'a beaucoup aidé et m'a convaincu de choisir ma structure d'état/réducteur d'origine de Ressource> Composant> App. Le guide est trop volumineux pour tenir ici, et vous voudrez probablement la version à jour ici .

Voici un bref aperçu de ce que je devais faire dans certains fichiers clés pour une application à deux composants, avec deux ressources de base (utilisateur et actif) avec des dérivés (listes) et des paramètres (recherche).

store/reducers/user/index.ts:

import { ActionReducer, combineReducers } from '@ngrx/store';

import { authenticatedUserReducer } from './authenticatedUser.reducer';
import { selectedUserReducer } from './selectedUser.reducer';
import { userListReducer } from './userList.reducer';
import { userSearchReducer } from './userSearch.reducer';
import { User } from '../../models';

const reducers = {
  authenticated: authenticatedUserReducer,
  selected: selectedUserReducer,
  list: userListReducer,
  search: userSearchReducer
};

interface UserState {
  authenticated: User,
  selected: User,
  list: User[],
  search: string
}

const reducer: ActionReducer<UserState> = combineReducers(reducers);

function userReducer(state: any, action: any) {
  return reducer(state, action);
}

export { userReducer, UserState };

store/reducers/asset/index.ts:

import { ActionReducer, combineReducers } from '@ngrx/store';

import { selectedAssetReducer } from './selectedAsset.reducer';
import { assetListReducer } from './assetList.reducer';
import { assetSearchReducer } from './assetSearch.reducer';
import { Asset } from '../../models';

const reducers = {
  selected: selectedAssetReducer,
  list: assetListReducer,
  search: assetSearchReducer
};

interface AssetState {
  selected: Asset,
  list: Asset[],
  search: string
}

const reducer: ActionReducer<AssetState> = combineReducers(reducers);

function assetReducer(state: any, action: any) {
  return reducer(state, action);
}

export { assetReducer, AssetState };

store/reducers/index.ts:

import { routerReducer, RouterState } from '@ngrx/router-store';

import { userReducer, UserState } from './user';
import { assetReducer, AssetState } from './asset';

const reducers = {
  router: routerReducer,
  user: userReducer,
  asset: assetReducer
};

interface AppState {
  router: RouterState,
  user: UserState,
  asset: AssetState
}

export { reducers, AppState };

Remarque: J'ai également inclus le réducteur de routeur fourni séparément.

app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';

import { StoreModule } from '@ngrx/store';
import { RouterStoreModule } from '@ngrx/router-store';

import { reducers } from './store';
import { AppComponent } from './app.component';
import { AppRoutes } from './app.routes';
import { HomeComponent } from './components/home/home.component';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot(AppRoutes),
    StoreModule.provideStore(reducers),
    RouterStoreModule.connectRouter()
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Remarque: utilisez ce dont vous avez besoin ici, supprimez ce que vous n'avez pas. J'ai fait un autre fichier index.ts dans/store, il exporte les réducteurs, tous les modèles et peut-être d'autres choses à l'avenir.

home.component.ts:

import { Component } from '@angular/core';

import { Observable } from 'rxjs/Observable';
import { Store } from '@ngrx/store';

import { AppState, User } from '../../store';

@Component({
  selector: 'home',
  templateUrl: './home.template.html'
})
export class HomeComponent {
  user: Observable<User>;

  constructor (private store: Store<AppState>) {
    this.user = store.select('user', 'selected');
    store.dispatch({ type: 'SET_USER_NAME', payload: 'Jesse' });
    store.dispatch({ type: 'ADD_USER_ROLE', payload: 'scientist' });
    store.dispatch({ type: 'ADD_USER_ROLE', payload: 'wordsmith' });
  }
}

Remarque: Vous pouvez tester avec quelque chose comme {{(user | async)?.name}} dans votre modèle.

Et c'est tout. Il peut y avoir de meilleures façons de le faire, je sais que j'aurais pu le faire avec un seul niveau par exemple (par exemple, juste les ressources de base), tout dépend de ce que vous pensez qui convient le mieux à votre application.

9
Kesarion