Angular 2
ngrx
수색…
소개
전체 예제 : 사용자 로그인 / 로그 아웃
선결 요건
이 주제는 Redux 및 / 또는 Ngrx에 관한 내용이 아닙니다 .
- Redux에 익숙해야합니다.
- 최소한 RxJs와 Observable 패턴의 기초를 이해하십시오.
먼저, 처음부터 예제를 정의하고 몇 가지 코드로 실행 해 봅시다.
개발자로서 저는 다음과 같이하고 싶습니다.
-
User
의 속성을 정의하는IUser
인터페이스가 있어야합니다. - 나중에
Store
에서User
를 조작하기 위해 사용할 액션을 선언하십시오. -
UserReducer
의 초기 상태 정의 - 감속기
UserReducer
만들기 - 우리의
UserReducer
를 메인 모듈로UserReducer
Store
를 만듭니다. -
Store
데이터를 사용하여보기에 정보를 표시하십시오.
Spoiler 경고 : 시작하기 전에 데모를 바로 보거나 코드를 읽으려면 Plunkr ( 임베드 뷰 또는 실행 뷰 )을 사용하십시오.
1) IUser
인터페이스 정의
인터페이스를 두 부분으로 나누고 싶습니다.
- 서버에서 가져올 속성
- UI에 대해서만 정의한 속성 (버튼이 회전해야하는 경우)
그리고 우리가 사용할 IUser
인터페이스는 다음과 같습니다.
user.interface.ts
export interface IUser {
// from server
username: string;
email: string;
// for UI
isConnecting: boolean;
isConnected: boolean;
};
2) User
조작을위한 액션 선언
이제 우리는 감속기 가 어떤 행동을 취해야하는지 생각해야합니다.
여기서 말하자 :
user.actions.ts
export const UserActions = {
// when the user clicks on login button, before we launch the HTTP request
// this will allow us to disable the login button during the request
USR_IS_CONNECTING: 'USR_IS_CONNECTING',
// this allows us to save the username and email of the user
// we assume those data were fetched in the previous request
USR_IS_CONNECTED: 'USR_IS_CONNECTED',
// same pattern for disconnecting the user
USR_IS_DISCONNECTING: 'USR_IS_DISCONNECTING',
USR_IS_DISCONNECTED: 'USR_IS_DISCONNECTED'
};
그러나 우리가 이러한 행동을 사용하기 전에, 우리가 왜 우리를 위해 그러한 행동 중 일부 를 파견 할 서비스가 필요한지 설명해주십시오.
사용자를 연결하려고한다고 가정 해 보겠습니다. 로그인 버튼을 클릭하면 다음과 같이 진행됩니다.
- 버튼을 클릭하십시오.
- 구성 요소가 이벤트를 catch하고
userService.login
호출합니다. -
userService.login
메서드dispatch
store 속성을 업데이트하기 위해 이벤트를dispatch
합니다.user.isConnecting
- HTTP 호출이 시작됩니다 ( 비동기 동작 을 시뮬레이트하기 위해 데모에서
setTimeout
을 사용합니다) -
HTTP
호출이 끝나면 사용자가 기록되었음을 스토어에 알리는 또 다른 작업을 전달합니다.
user.service.ts
@Injectable()
export class UserService {
constructor(public store$: Store<AppState>) { }
login(username: string) {
// first, dispatch an action saying that the user's tyring to connect
// so we can lock the button until the HTTP request finish
this.store$.dispatch({ type: UserActions.USR_IS_CONNECTING });
// simulate some delay like we would have with an HTTP request
// by using a timeout
setTimeout(() => {
// some email (or data) that you'd have get as HTTP response
let email = `${username}@email.com`;
this.store$.dispatch({ type: UserActions.USR_IS_CONNECTED, payload: { username, email } });
}, 2000);
}
logout() {
// first, dispatch an action saying that the user's tyring to connect
// so we can lock the button until the HTTP request finish
this.store$.dispatch({ type: UserActions.USR_IS_DISCONNECTING });
// simulate some delay like we would have with an HTTP request
// by using a timeout
setTimeout(() => {
this.store$.dispatch({ type: UserActions.USR_IS_DISCONNECTED });
}, 2000);
}
}
3) UserReducer
의 초기 상태 정의
user.state.ts
export const UserFactory: IUser = () => {
return {
// from server
username: null,
email: null,
// for UI
isConnecting: false,
isConnected: false,
isDisconnecting: false
};
};
4) 감속기 UserReducer
만들기
감속기는 2 가지 인수를 취합니다.
- 현재 상태
-
Action
유형의Action<{type: string, payload: any}>
알림 : 감속기는 어느 시점에서 초기화해야합니다.
3 장에서 감속기의 기본 상태를 정의 했으므로 다음과 같이 사용할 수 있습니다.
user.reducer.ts
export const UserReducer: ActionReducer<IUser> = (user: IUser, action: Action) => {
if (user === null) {
return userFactory();
}
// ...
}
다행히도 우리의 factory
함수를 사용하여 객체를 반환하고 감속기 내에서 (ES6) 기본 매개 변수 값 을 사용하여이를 작성하는 더 쉬운 방법이 있습니다.
export const UserReducer: ActionReducer<IUser> = (user: IUser = UserFactory(), action: Action) => {
// ...
}
그런 다음 감속기의 모든 작업을 처리해야합니다. 팁 : ES6 Object.assign
함수를 사용하여 상태를 불변으로 유지합니다.
export const UserReducer: ActionReducer<IUser> = (user: IUser = UserFactory(), action: Action) => {
switch (action.type) {
case UserActions.USR_IS_CONNECTING:
return Object.assign({}, user, { isConnecting: true });
case UserActions.USR_IS_CONNECTED:
return Object.assign({}, user, { isConnecting: false, isConnected: true, username: action.payload.username });
case UserActions.USR_IS_DISCONNECTING:
return Object.assign({}, user, { isDisconnecting: true });
case UserActions.USR_IS_DISCONNECTED:
return Object.assign({}, user, { isDisconnecting: false, isConnected: false });
default:
return user;
}
};
5) 우리의 UserReducer
를 메인 모듈로 UserReducer
Store
를 구축하십시오
app.module.ts
@NgModule({
declarations: [
AppComponent
],
imports: [
// angular modules
// ...
// declare your store by providing your reducers
// (every reducer should return a default state)
StoreModule.provideStore({
user: UserReducer,
// of course, you can put as many reducers here as you want
// ...
}),
// other modules to import
// ...
]
});
6) Store
데이터를 사용하여 정보를 표시합니다.
이제 모든 것이 로직 측면에서 준비가되었으며 우리가 원하는 것을 두 가지 구성 요소로 표시하면됩니다.
-
UserComponent
: [덤 구성 요소]@Input
속성과async
파이프를 사용하여 상점에서 사용자 개체를 전달합니다. 이렇게하면 구성 요소는 사용 가능한 경우에만 사용자를받습니다 (user
는Observable<IUser>
유형이 아닌IUser
유형이됩니다). -
LoginComponent
[Smart component] 우리는Store
를이 구성 요소에 직접 삽입하고Observable
로서user
에게만 작업합니다.
user.component.ts
@Component({
selector: 'user',
styles: [
'.table { max-width: 250px; }',
'.truthy { color: green; font-weight: bold; }',
'.falsy { color: red; }'
],
template: `
<h2>User information :</h2>
<table class="table">
<tr>
<th>Property</th>
<th>Value</th>
</tr>
<tr>
<td>username</td>
<td [class.truthy]="user.username" [class.falsy]="!user.username">
{{ user.username ? user.username : 'null' }}
</td>
</tr>
<tr>
<td>email</td>
<td [class.truthy]="user.email" [class.falsy]="!user.email">
{{ user.email ? user.email : 'null' }}
</td>
</tr>
<tr>
<td>isConnecting</td>
<td [class.truthy]="user.isConnecting" [class.falsy]="!user.isConnecting">
{{ user.isConnecting }}
</td>
</tr>
<tr>
<td>isConnected</td>
<td [class.truthy]="user.isConnected" [class.falsy]="!user.isConnected">
{{ user.isConnected }}
</td>
</tr>
<tr>
<td>isDisconnecting</td>
<td [class.truthy]="user.isDisconnecting" [class.falsy]="!user.isDisconnecting">
{{ user.isDisconnecting }}
</td>
</tr>
</table>
`
})
export class UserComponent {
@Input() user;
constructor() { }
}
login.component.ts
@Component({
selector: 'login',
template: `
<form
*ngIf="!(user | async).isConnected"
#loginForm="ngForm"
(ngSubmit)="login(loginForm.value.username)"
>
<input
type="text"
name="username"
placeholder="Username"
[disabled]="(user | async).isConnecting"
ngModel
>
<button
type="submit"
[disabled]="(user | async).isConnecting || (user | async).isConnected"
>Log me in</button>
</form>
<button
*ngIf="(user | async).isConnected"
(click)="logout()"
[disabled]="(user | async).isDisconnecting"
>Log me out</button>
`
})
export class LoginComponent {
public user: Observable<IUser>;
constructor(public store$: Store<AppState>, private userService: UserService) {
this.user = store$.select('user');
}
login(username: string) {
this.userService.login(username);
}
logout() {
this.userService.logout();
}
}
Ngrx
는 Redux
와 RxJs
개념을 통합 한 Ngrx
때문에 Ngrx
에는 RxJs
이해하는 것이 매우 어려울 수 있습니다. 그러나 이것은이 예에서 알 수 있듯이 반응 형 앱을 제공 하고 데이터를 쉽게 공유 할 수있는 강력한 패턴입니다. Plunkr을 사용할 수 있다는 것을 잊지 말고 자신의 테스트를 위해 포크로 만들 수 있습니다!
나는 그것이 tho 주제가 아주 길다는 것에 도움이 되었기를 바랍니다, 환호!