web-dev-qa-db-ja.com

Ngxs-バックエンドからデータをロードするアクション/状態

私はngxsの実験を始めましたが、これまでの読み取りから、データを永続化して読み取るためにAPIにコールバックする場所について100%明確ではありません(私が見たすべての例はそれをしない、またはいくつかのモックを使用する)。

例えば。アイテムのリストを保持する状態を作成しました。アイテムを追加するときは、「AddItem」アクションをストアにディスパッチし、そこで新しいアイテムを状態に追加します。これはすべて正常に動作します-質問は、アイテムをサーバーにPOSTする呼び出しをプラグインする適切な場所はどこですか?

アクションの実装で、つまりストアのアイテムリストを更新する直前にAPIを呼び出す必要があります。

または、Angularコンポーネント(サービス経由)でAPIを呼び出し、応答を受け取ったときに 'アイテムの追加'アクションをディスパッチしますか?

私はこの分野にはまったく慣れていないので、これらのアプローチのガイダンスや賛否両論は素晴らしいでしょう。

15
Garth Mason

最適な場所は、アクションハンドラーです。

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')),
    );
  }
}

上記の例では、APIを返すための明示的なアクションはありません。AddTodoアクションレスポンスに基づいて状態を変更します。

必要に応じて、より明確にするために3つのアクションに分割できます。

AddTodoAddTodoCompleteおよびAddTodoFailure

この場合、http投稿から新しいイベントをディスパッチする必要があります。

19
Leon Radley

エフェクトをストアから分離する場合は、基本状態クラスを定義できます。

@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
       });
    }
}

次に、その状態から派生し、派生クラスにサービスロジックを配置します。

@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 ));
    }
}

私が調べたNGXSの例では、このアプローチを見たことはありませんが、UIとバックエンドという2つの懸念事項を分離する方法を探していました。

2
Zoran