Star wars API를 사용한 Angular NGRX
NgRx란?
NgRx는 Angular에서 반응형 애플리케이션을 구축하기 위한 프레임워크입니다. NgRx는 다음에 대한 라이브러리를 제공합니다.
가게
NgRx Store는 상태 변경을 표현하기 위해 단일 상태 및 작업을 사용하여 유지 관리 가능하고 명시적인 응용 프로그램을 만들기 위한 상태 관리를 제공합니다. 상태를 관리하기 위해 애플리케이션 전반에 걸친 글로벌 솔루션이 필요하지 않은 경우 로컬 상태 관리를 위한 솔루션을 제공하는 NgRx ComponentStore를 사용하는 것이 좋습니다.
효과
서비스 기반 Angular 애플리케이션에서 구성 요소는 서비스를 통해 직접 외부 리소스와 상호 작용합니다. 대신 효과는 해당 서비스와 상호 작용하고 구성 요소에서 격리하는 방법을 제공합니다. 효과는 데이터 가져오기, 여러 이벤트를 생성하는 장기 실행 작업, 구성 요소가 이러한 상호 작용에 대한 명시적인 지식이 필요하지 않은 기타 외부 상호 작용과 같은 작업을 처리하는 곳입니다.
감속기
상태 인터페이스, 감속기의 초기 상태 개체 및 감속기 함수를 포함하는 감속기 파일을 생성합니다.
행위
액션은 NgRx의 주요 빌딩 블록 중 하나입니다. 작업은 애플리케이션 전체에서 발생하는 고유한 이벤트를 나타냅니다. 페이지와의 사용자 상호 작용, 네트워크 요청을 통한 외부 상호 작용 및 장치 API와의 직접적인 상호 작용에서 이러한 이벤트 및 더 많은 이벤트가 작업으로 설명됩니다.
앱을 만들겠습니다. 먼저 "시작하기"링크here를 찾을 수 있는 각도 앱을 만들어야 합니다. 백엔드의 경우SWAPI를 사용합니다.
애플리케이션용 감속기를 생성하겠습니다. 먼저 "reducers"폴더를 생성하고 내부에 index.ts 파일을 생성해야 합니다.
import {
ActionReducerMap,
createFeatureSelector,
createSelector,
MetaReducer
} from '@ngrx/store';
import { environment } from '../../environments/environment';
import * as fromMovies from '../movies/movies.reducer';
export interface State {
movies: fromMovies.State;
}
export const reducers: ActionReducerMap<State> = {
movies: fromMovies.reducer,
};
export const metaReducers: MetaReducer<State>[] = !environment.production ? [] : [];
export const getMoviesState = createFeatureSelector<fromMovies.State>('movies');
export const getMovies = createSelector(getMoviesState, state => state.data);
export const getIsLoading = createSelector(getMoviesState, state => state.isLoading);
export const getMovieCharacters = createSelector(getMoviesState, state => state.selectedMovieCharacters);
export const getMovie = createSelector(getMoviesState, state => state.selectedMovie);
export const getCharacterMovies = createSelector(getMoviesState, state => state.selectedCharacterMovies);
export const getCharacter = createSelector(getMoviesState, state => state.selectedCharacter);
// export const getCurrentPage = createSelector(getMoviesState, state => state.page);
// export const getIsFirstPage = createSelector(getMoviesState, state => !state.previous);
// export const getIsLastPage = createSelector(getMoviesState, state => !state.next);
앱 내부에 캐릭터와 영화 구성 요소를 생성합니다(github에서 코드를 찾을 수 있음). 이 기사에서는 ngrx 부분을 보여주고 싶습니다. 다음 단계는 movies.effects.ts를 생성하는 것입니다.
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store';
// import { getCurrentPage } from '../reducers/index';
import { State } from './movies.reducer';
import { MovieService } from './movie.service';
import {
MoviesActionTypes,
MoviesActions,
FetchMovies,
FetchMoviesSuccess,
FetchMoviesError,
FetchMovieCharactersSuccess,
FetchMovieCharactersError,
FetchMovieError,
FetchMovieSuccess,
FetchCharacterError,
FetchCharacterSuccess,
FetchCharacterMoviesSuccess,
FetchCharacterMoviesError
} from './movies.actions';
import { Observable, of } from 'rxjs';
import { map, switchMap, catchError, withLatestFrom } from 'rxjs/operators';
import { CharactersService } from '../characters/characters.service';
import { Movie } from './models/movie';
@Injectable()
export class MoviesEffects {
fetch$ = createEffect(() => {
return this.actions$
.pipe(
ofType(MoviesActionTypes.FetchMovies),
withLatestFrom(this.store),
switchMap(([action, state]) =>
this.service.getMovies().pipe(
map(data => new FetchMoviesSuccess(data)),
catchError(err => of(new FetchMoviesError(err)))
)
)
)
});
fetchCharacters$ = createEffect(() => {
return this.actions$
.pipe(
ofType(MoviesActionTypes.FetchMovieCharacters),
withLatestFrom(this.store),
switchMap(([action, state]) =>
this.charactersService.getCharactersByFilm(this.service.selectedFilm).pipe(
catchError(err => of(new FetchMovieCharactersError(err))),
map(data =>
new FetchMovieCharactersSuccess(data)
// (characters: Movie['charactersData']) => {
// console.log("characters:", characters);
// // this.movieService.selectedFilm.charactersData=[];
// console.log("this.movieService.selectedFilm.charactersData:", this.movieService.selectedFilm.charactersData);
// // this.movieService.selectedFilm.charactersData = characters;
// return true;
// }
)
)
))
});
fetchCharacterMovies$ = createEffect(() => {
return this.actions$
.pipe(
ofType(MoviesActionTypes.FetchCharacterMovies),
withLatestFrom(this.store),
switchMap(([action, state]) =>
this.service.getFilmsByCharacter(this.service.selectedCharacter).pipe(
catchError(err => of(new FetchCharacterMoviesError(err))),
map(data =>
new FetchCharacterMoviesSuccess(data)
// (characters: Movie['charactersData']) => {
// console.log("characters:", characters);
// // this.movieService.selectedFilm.charactersData=[];
// console.log("this.movieService.selectedFilm.charactersData:", this.movieService.selectedFilm.charactersData);
// // this.movieService.selectedFilm.charactersData = characters;
// return true;
// }
)
)
))
});
fetchMovie$ = createEffect(() => {
return this.actions$
.pipe(
ofType(MoviesActionTypes.FetchMovie),
withLatestFrom(this.store),
switchMap(([action, state]) =>
this.service.getFilm(this.service.selectedFilm.id).pipe(
catchError(err => of(new FetchMovieError(err))),
map(data =>
new FetchMovieSuccess(data)
// (characters: Movie['charactersData']) => {
// console.log("characters:", characters);
// // this.movieService.selectedFilm.charactersData=[];
// console.log("this.movieService.selectedFilm.charactersData:", this.movieService.selectedFilm.charactersData);
// // this.movieService.selectedFilm.charactersData = characters;
// return true;
// }
)
)
))
});
// this.service.getMovies().pipe(
// map(data =>
// new FetchMovieCharactersSuccess(data)
// ),
// catchError(err => of(new FetchMovieCharactersError(err)))
// )
// )
// );
fetchCharacter$ = createEffect(() => {
return this.actions$
.pipe(
ofType(MoviesActionTypes.FetchCharacter),
withLatestFrom(this.store),
switchMap(([action, state]) =>
this.charactersService.getCharacter(this.service.selectedCharacter.id).pipe(
catchError(err => of(new FetchCharacterError(err))),
map(data =>
new FetchCharacterSuccess(data)
)
)
))
});
paginate$ = createEffect(() => {
return this.actions$
.pipe(
ofType(MoviesActionTypes.ChangePage),
map(() => new FetchMovies())
)
});
constructor(private actions$: Actions,
private store: Store<State>,
private service: MovieService,
private charactersService: CharactersService) { }
}
createEffect 함수를 사용합니다(Observable 및 EffectConfig에서 효과 생성).
movie.reducer.ts의 경우 아래 코드를 사용합니다.
import { Action } from '@ngrx/store';
import { MoviesActions, MoviesActionTypes, Pagination } from './movies.actions';
import { Movie } from './models/movie';
import { HttpErrorResponse } from '@angular/common/http';
import { Character } from '../characters/models/character';
export interface State {
isLoading: boolean;
error: HttpErrorResponse | null;
data: Movie[] | null;
selectedMovieCharacters: [] | null;
selectedMovie:Movie| null;
selectedCharacterMovies: [] | null;
selectedCharacter: Character | null;
// next: string | null;
// previous: string | null;
}
export const initialState: State = {
isLoading: false,
error: null,
data: [],
selectedMovieCharacters:[],
selectedMovie: null,
selectedCharacter: null,
selectedCharacterMovies:[]
// next: null,
// previous: null,
};
export function reducer(state = initialState, action: MoviesActions): State {
switch (action.type) {
case MoviesActionTypes.FetchMovies:
return {
...state,
isLoading: true,
error: null
};
case MoviesActionTypes.FetchMoviesSuccess:
return {
...state,
isLoading: false,
data: action.payload,
// next: action.payload.next,
// previous: action.payload.previous
};
case MoviesActionTypes.FetchMoviesError:
return {
...state,
isLoading: false,
error: action.payload
};
case MoviesActionTypes.FetchMovie:
return {
...state,
isLoading: true,
error: null
};
case MoviesActionTypes.FetchMovieSuccess:
return {
...state,
isLoading: false,
selectedMovie: action.payload,
// next: action.payload.next,
// previous: action.payload.previous
};
case MoviesActionTypes.FetchMovieError:
return {
...state,
isLoading: false,
error: action.payload
};
case MoviesActionTypes.FetchCharacter:
return {
...state,
isLoading: true,
error: null
};
case MoviesActionTypes.FetchCharacterSuccess:
return {
...state,
isLoading: false,
selectedCharacter: action.payload,
// next: action.payload.next,
// previous: action.payload.previous
};
case MoviesActionTypes.FetchCharacterError:
return {
...state,
isLoading: false,
error: action.payload
};
case MoviesActionTypes.FetchCharacterMovies:
return {
...state,
isLoading: true,
error: null
};
case MoviesActionTypes.FetchCharacterMoviesSuccess:
return {
...state,
isLoading: false,
selectedCharacterMovies: action.payload,
// next: action.payload.next,
// previous: action.payload.previous
};
case MoviesActionTypes.FetchCharacterMoviesError:
return {
...state,
isLoading: false,
error: action.payload
};
case MoviesActionTypes.FetchMovieCharacters:
return {
...state,
isLoading: true,
error: null
};
case MoviesActionTypes.FetchMovieCharactersSuccess:
return {
...state,
isLoading: false,
selectedMovieCharacters: action.payload,
// next: action.payload.next,
// previous: action.payload.previous
};
case MoviesActionTypes.FetchMovieCharactersError:
return {
...state,
isLoading: false,
error: action.payload
};
// case MoviesActionTypes.ChangePage:
// return {
// ...state,
// page: action.payload === Pagination.NEXT ? ++state.page : --state.page
// };
default:
return state;
}
}
우리는 movie.action.ts를 만들 것입니다
import { Action } from '@ngrx/store';
import { Movie, MoviesResponse } from './models/movie';
import { HttpErrorResponse } from '@angular/common/http';
export const enum MoviesActionTypes {
FetchMovies = '[Movies] Fetch Movies',
FetchMoviesSuccess = '[Movies] Load Movies Success',
FetchMoviesError = '[Movies] Load Movies Error',
ChangePage = '[Movies] Change page',
FetchMovieCharacters = '[Movie] Fetch Movie Characters',
FetchMovieCharactersSuccess = `[Movie] Load Movie Characters Success`,
FetchMovieCharactersError = '[Movie] Load Movie Characters Error',
FetchMovie = '[Movie] Fetch Movie ',
FetchMovieSuccess = `[Movie] Load Movie Success`,
FetchMovieError = '[Movie] Load Movie Error',
FetchCharacter = '[Character] Fetch Character ',
FetchCharacterSuccess = `[Character] Load Character Success`,
FetchCharacterError = '[Character] Load Character Error',
FetchCharacterMovies = '[Character] Fetch Character Movies ',
FetchCharacterMoviesSuccess = `[Character] Load Character Movies Success`,
FetchCharacterMoviesError = '[Character] Load Character Movies Error',
}
export const enum Pagination {
NEXT,
PREV
}
export class FetchMovies implements Action {
readonly type = MoviesActionTypes.FetchMovies;
}
export class FetchMoviesSuccess implements Action {
readonly type = MoviesActionTypes.FetchMoviesSuccess;
constructor(public payload: Movie[]) { }
}
export class FetchMoviesError implements Action {
readonly type = MoviesActionTypes.FetchMoviesError;
constructor(public payload: HttpErrorResponse) { }
}
export class FetchMovie implements Action {
readonly type = MoviesActionTypes.FetchMovie;
constructor() {
// console.log("*************FetchMovie*************");
}
}
export class FetchCharacterSuccess implements Action {
readonly type = MoviesActionTypes.FetchCharacterSuccess;
constructor(public payload: any) {
// console.log("FetchMovieSuccess");
}
}
export class FetchCharacterError implements Action {
readonly type = MoviesActionTypes.FetchCharacterError;
constructor(public payload: HttpErrorResponse) { }
}
export class FetchCharacter implements Action {
readonly type = MoviesActionTypes.FetchCharacter;
constructor() {
// console.log("*************FetchCharacter*************");
}
}
export class FetchCharacterMoviesSuccess implements Action {
readonly type = MoviesActionTypes.FetchCharacterMoviesSuccess;
constructor(public payload: any) {
}
}
export class FetchCharacterMoviesError implements Action {
readonly type = MoviesActionTypes.FetchCharacterMoviesError;
constructor(public payload: HttpErrorResponse) { }
}
export class FetchCharacterMovies implements Action {
readonly type = MoviesActionTypes.FetchCharacterMovies;
constructor() {
// console.log("*************FetchCharacter*************");
}
}
export class FetchMovieSuccess implements Action {
readonly type = MoviesActionTypes.FetchMovieSuccess;
constructor(public payload: any) {
// console.log("FetchMovieSuccess");
}
}
export class FetchMovieError implements Action {
readonly type = MoviesActionTypes.FetchMovieError;
constructor(public payload: HttpErrorResponse) { }
}
export class FetchMovieCharacters implements Action {
readonly type = MoviesActionTypes.FetchMovieCharacters;
}
export class FetchMovieCharactersSuccess implements Action {
readonly type = MoviesActionTypes.FetchMovieCharactersSuccess;
constructor(public payload: any) {
// console.log("FetchMovieCharactersSuccess");
}
}
export class FetchMovieCharactersError implements Action {
readonly type = MoviesActionTypes.FetchMovieCharactersError;
constructor(public payload: HttpErrorResponse) { }
}
export class ChangePage implements Action {
readonly type = MoviesActionTypes.ChangePage;
constructor(public payload: Pagination) { }
}
export type MoviesActions = FetchMovieCharacters
| FetchMovieCharactersSuccess
| FetchMovieCharactersError
| FetchMovies
| FetchMoviesSuccess
| FetchMoviesError
| FetchMovie
| FetchMovieSuccess
| FetchMovieError
| FetchCharacter
| FetchCharacterSuccess
| FetchCharacterError
| FetchCharacterMovies
| FetchCharacterMoviesSuccess
| FetchCharacterMoviesError
| ChangePage;
영화 목록 구성 요소, 영화 세부 구성 요소 및 기타 캐릭터 구성 요소의 경우 이 githublink에서 전체 프로젝트를 찾을 수 있습니다.
프로젝트를 실행하면 "스타워즈"영화 목록을 볼 수 있습니다.
목록 앱에서 영화를 클릭하면 영화 세부 정보와 캐릭터 목록이 표시됩니다.
캐릭터 이름 앱을 클릭하면 캐릭터 세부 정보 페이지로 이동하고 캐릭터 세부 정보와 캐릭터가 존재하는 영화 목록을 표시합니다.
영화 이름을 클릭하면 영화 세부 정보 페이지로 이동합니다. 응용 프로그램은 ngrx 상태를 사용하여 영화 및 캐릭터와 관련된 모든 프로세스를 작업합니다.
이 기사가 ngrx를 이해하는 데 도움이 되기를 바랍니다.
Reference
이 문제에 관하여(Star wars API를 사용한 Angular NGRX), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/ilyoskhuja/angular-ngrx-with-star-wars-api-465a텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)