web-dev-qa-db-fra.com

Ngxs - Actions / état pour charger les données depuis le backend

Je viens de commencer à expérimenter avec ngxs mais à ce jour, je ne sais pas à 100% où je devrais appeler mon API pour persister et lire les données (tous les exemples que j'ai vus sont soit pas le faire, ou en utilisant une maquette).

Par exemple. J'ai créé un état où je maintiens une liste d'articles. Lorsque je veux ajouter un élément, j'envoie l'action "AddItem" au magasin, où j'ajoute ce nouvel élément à l'état. Tout fonctionne bien - la question est de savoir où est l'endroit approprié pour brancher l'appel qui POSTE l'élément au serveur?

Dois-je appeler l'API dans mon implémentation d'action, c'est-à-dire juste avant de mettre à jour la liste d'articles du magasin.

Ou dois-je appeler l'API dans mon composant Angular (via un service), puis envoyer l'action "Ajouter un élément" lorsque j'ai reçu une réponse?

Je suis assez nouveau dans ce domaine, donc tout conseil ou pour/contre de ces approches serait formidable.

15
Garth Mason

Le meilleur endroit est dans votre gestionnaire d'actions.

import { HttpClient } from '@angular/common/http';
import { State, Action, StateContext } from '@ngxs/store';
import { tap, catchError } from 'rxjs/operators';

//
// todo-list.actions.ts
//
export class AddTodo {
  static readonly type = '[TodoList] AddTodo';
  constructor(public todo: Todo) {}
}


//
// todo-list.state.ts
//
export interface Todo {
  id: string;
  name: string;
  complete: boolean;
}
​
export interface TodoListModel {
  todolist: Todo[];
}
​
@State<TodoListModel>({
  name: 'todolist',
  defaults: {
    todolist: []
  }
})
export class TodoListState {

  constructor(private http: HttpClient) {}
​
  @Action(AddTodo)
  feedAnimals(ctx: StateContext<TodoListModel>, action: AddTodo) {

    // ngxs will subscribe to the post observable for you if you return it from the action
    return this.http.post('/api/todo-list').pipe(

      // we use a tap here, since mutating the state is a side effect
      tap(newTodo) => {
        const state = ctx.getState();
        ctx.setState({
          ...state,
          todolist: [ ...state.todolist, newTodo ]
        });
      }),
      // if the post goes sideways we need to handle it
      catchError(error => window.alert('could not add todo')),
    );
  }
}

Dans l'exemple ci-dessus, nous n'avons pas d'action explicite pour le retour de l'API, nous mutons l'état en fonction de la réponse des actions AddTodo.

Si vous le souhaitez, vous pouvez le diviser en trois actions pour être plus explicite,

AddTodo, AddTodoComplete et AddTodoFailure

Dans ce cas, vous devrez envoyer de nouveaux événements à partir de la publication http.

19
Leon Radley

Si vous souhaitez séparer l'effet du magasin, vous pouvez définir une classe d'état de base:

@State<Customer>( {
    name: 'customer'
})
export class CustomerState {
    constructor() { }

    @Action(ChangeCustomerSuccess)
    changeCustomerSuccess({ getState, setState }: StateContext<Customer>, { payload }: ChangeCustomerSuccess ) {
        const state = getState();
       // Set the new state. No service logic here.
       setState( {
           ...state,
           firstname: payload.firstname, lastname: lastname.nachname
       });
    }
}

Ensuite, vous dérivez de cet état et placez votre logique de service dans la classe dérivée:

@State<Customer>({
    name: 'customer'
})
export class CustomerServiceState extends CustomerState {

    constructor(private customerService: CustomerService, private store: Store) {
        super();
    }

    @Action(ChangeCustomerAction)
    changeCustomerService({ getState, setState }: StateContext<Customer>, { payload }: ChangeCustomerAction) {

        // This action does not need to change the state, but it can, e.g. to set the loading flag.
        // It executes the (backend) effect and sends success / error to the store.

        this.store.dispatch( new ChangeCustomerSuccess( payload ));
    }
}

Je n'ai vu cette approche dans aucun des exemples NGXS que j'ai consultés, mais je cherchais un moyen de séparer ces deux préoccupations - l'interface utilisateur et le backend.

2
Zoran