Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Actions/state to load data from backend

Tags:

angular

ngxs

I've just started experimenting with ngxs but from my reading so far I'm not 100% clear on where I should be calling back to my API to persist and read data (all examples I've seen are either not doing it, or using some mock).

E.g. I've created a state where I maintain a list of items. When I want to add an item, I dispatch the 'AddItem` action to the store, where I add that new item to the state. This all works ok - the question is where is the appropriate place to plug in the call that POSTs the item to the server?

Should I call the API in my action implementation i.e. just before I update the store's item list.

Or should I call the API in my Angular component (via a service), then dispatch the 'Add Item' action when I received a response?

I'm quite new to this area, so any guidance or pros/cons of these approaches would be great.

like image 823
Garth Mason Avatar asked Apr 23 '18 06:04

Garth Mason


2 Answers

The best place is in your action handler.

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

In the example above we don't have a explicit action for the return of the api, we mutate the state based on the AddTodo actions response.

If you want you can split it into three actions to be more explicit,

AddTodo, AddTodoComplete and AddTodoFailure

In which case you will need to dispatch new events from the http post.

like image 173
Leon Radley Avatar answered Oct 18 '22 00:10

Leon Radley


If you want to separate the effect from the store you could define a base state class:

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

Then you would derive from that state and put your service logic in the derived class:

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

I have not seen this approach in any of the NGXS examples I looked through, but I was looking for a way to separate those two concerns - UI and backend.

like image 4
Zoran Avatar answered Oct 18 '22 00:10

Zoran