Search code examples
angularreduxstateangular7ngxs

NGXS state management : change the state when an action is called and save it to DB


I have several movies, which one can be updated, deleted and liked through NGXS state management pattern.

I want to update my store to show if a movie is liked or not and save the state in the database.

Update and delete actions work, but the like action does not work, I don't understand why. It can't save in DB. If someone can help me I'm stuck. Below is my code, thanks.

db.json

"movies": [
{
  "id": 1,
  "title": "Avengers Infinity War",
  "imageUrl": "https://fakeimg.pl/600x600/000000/fff",
  "liked": (here I want to change the status to true or false when clicked)

},
{
  "id": 2,
  "title": "Avengers Endgame",
  "imageUrl": "https://fakeimg.pl/600x600/000000/fff"
}

]

Movie.ts

 export interface Movie {
 id: number;
 title: string;
 imageUrl: string;
 liked: boolean;
}

movie.action.ts

 export class UpdateMovie {
      static readonly type = '[Movie] UpdateMovie';
      constructor(public payload: Movie, public id: number) {}
    }

 export class DeleteMovie {
     static readonly type = '[Movie] DeleteMovie';
     constructor(public id: number) {}
    }

  export class LikeMovie {
      static readonly type = '[Movie] LikeMovie';
      constructor(public payload: Movie) {}
    }

movie-service.ts

 updateMovie(payload: Movie, id: number) {
    return this.httpClient
     .put<Movie>(encodeURI(this.URL_BASE + `movies/${id}`), payload)
   }

deleteMovie(id: number) {
  return this.httpClient
    .delete(encodeURI(this.URL_BASE + `movies/${id}`))
  }

likeMovie(payload: Movie) {
    return this.httpClient
      .post<Movie>(encodeURI(this.URL_BASE + `movies`), payload);
   }

movie.state.ts

   export class MovieStateModel {
       movies: Movie[];
       selectedMovie: Movie;
       liked: Movie[];
     }

   @State<MovieStateModel>({
      name: 'movies',
      defaults: {
      movies: [],
      selectedMovie: null,
      liked: []
    }
  })

   @Action(DeleteMovie)
   deleteMovie({ getState, setState }: StateContext<MovieStateModel>, {id}: DeleteMovie) {
    return this.movieService.deleteMovie(id).pipe(tap(() => {
      const state = getState();
      setState({
        ...state,
        movies: state.movies.filter(item => item.id !== id)
      });
    }));
   }

 @Action(UpdateMovie)
 updateMovie({ getState, setState }: StateContext<MovieStateModel> {payload, id }: UpdateMovie) {
    return this.movieService.updateMovie(payload, id).pipe(tap((result) =>
  {
      const state = getState();
      const movieList = [...state.movies];
      const movieIndex = movieList.findIndex(item => item.id === id);
      movieList[movieIndex] = result;
      setState({
         ...state,
         movies: movieList
       });
     }));
    }

 @Action(LikeMovie)
 LikeMovie({ getState, setState }: StateContext<MovieStateModel>, {payload}: LikeMovie) {
    return this.movieService.likeMovie(payload).pipe(tap(() => {
      const state = getState();
      setState({
         ...state,
         liked: [...state.liked, payload]
      });
    }));
  }

movie-list.component.ts

  likeMovie(payload: Movie) {
  this.store.dispatch(new LikeMovie(payload));
}

Solution

  • Ok so I have done some tests, and I have removed the request to the DB to make it simple.

    Based on my Movie model, I want to update the liked property of a movie to true or false when I call the Like action, but instead it creates a new state called liked with the movie in it... I know I'm doing something wrong, I can't find a proper answer in the official doc and I'm new with NGXS.

    Below are my files:

    Movie.ts

    export interface Movie {
      id: number;
      title: string;
      imageUrl: string;
      liked: boolean;
    }
    

    movie.states.ts

    export class MovieStateModel {
      movies: Movie[];
      selectedMovie: Movie;
    }
    
    @State<MovieStateModel>({
      name: 'movies',
      defaults: {
        movies: [],
        selectedMovie: null
      }
    })
    
    @Action(LikeMovie)
      LikeMovie({ getState, setState }: StateContext<MovieStateModel>, { payload }: LikeMovie) {
        const state = getState();
        setState({
          ...state,
          liked: payload
        });
      }
    

    movie.actions.ts

    export class LikeMovie {
      static readonly type = '[Movie] LikeMovie';
    
      constructor(public payload: Movie) {}
    }
    

    movie-list.component.ts

    likeMovie(payload: Movie) {
        this.store.dispatch(new LikeMovie(payload));
      }