왜 그리고 각 반응 형식의 상태를 어떻게 관리하는지
26918 단어 ngrxformsangularstateadapt
setValue()
와disable()
등 방법은 적어도 외부에서 폼 상태를 관리해야 한다는 것을 보여준다.그러나 이런 방법은 모두 필수적이기 때문에 통상적으로 다른 방법과 같이 좀 더 성명적이고 반응적인 방법이 있는 것이 가장 좋다.이 점을 어떻게 실현하는지 연구하기 전에 반응식 상태 관리가 특히 유용한 장면들을 살펴보자.
저장된 진행 상태
이것은 사용자가 하나의 폼에 많은 시간을 소비하고 여러 개의 초고 사이를 전환하는 프로그램입니다. 예를 들어 초고 전자메일이 있는 전자메일 클라이언트입니다.이것은 폼에 대량의 외부 상태 업데이트를 해야 한다.
취소/다시 실행
여러 개의 폼 요소를 뛰어넘어 취소/재작업을 하는 것은 사용자에게 매우 편리하고 여러 번 폼 상태를 완전히 교환해야 한다.
시간 및 실시간
때때로 실시간 데이터는 표에 지속적으로 반영되어야 한다. 예를 들어 사용자가 빈번하게 가격을 갱신하는 상황에서 특정한 프로젝트에 대해 입찰을 하거나 머지않아 특정한 프로젝트를 배정해야 한다.
서버측 이벤트
사용자가 편집 중인 데이터 모델에 문제가 생겼을 때, 폼의 상태를 업데이트해야 할 수도 있습니다.이 이벤트는 다른 사용자가 같은 데이터를 편집하거나, 물건이 매진되고, 일부 상태가 바뀌거나, 사용자가 오래된 데이터를 제출하려고 하면 문제가 발생할 수 있는 모든 이벤트일 수 있습니다.
복잡성
복잡한 폼은 이해하기 어렵지만 레드ux 개발 도구는 많은 도움을 줄 수 있다.외부 상태 관리를 설정할 시간이 충분하도록 폼이 얼마나 복잡한지 직접 결정해야 합니다.
어떻게 써요?
간단한 방법
폼에 외부 상태 관리를 설정하는 데 걸리는 시간이 생각보다 적을 수도 있습니다.NgRx와 StateAdapt를 동시에 사용하는 방법을 설명하겠습니다. 제가 방금 StateAdapt를 발표했기 때문에 얼마나 멋있는지 알아주셨으면 좋겠습니다.😁
양식과 관련된 내용만 보려면 5-7단계로 건너뜁니다.
1단계(NgRx 및 StateAdapt)
양식 상태 인터페이스와 초기 상태를 만들려면 다음과 같이 하십시오.
// form-state.interface.ts
export interface FormState { // Whatever it is
name: string;
age: number;
}
export const initialState: FormState = { name: '', age: 0 };
2단계(NgRx에만 해당)
이 작업을 만들려면 다음과 같이 하십시오.
// form.actions.ts
import { createAction, props } from '@ngrx/store';
import { FormState } from './form-state.interface';
export const formValueChange = createAction(
'[Form] Value Change',
props<FormState>()
);
3단계(NgRx에만 해당)
감속기를 생성하려면 다음과 같이 하십시오.
// form.reducer.ts
import { Action, createReducer, on } from "@ngrx/store";
import { FormState, initialState } from "./form-state.interface";
import { formValueChange } from "./form.actions";
const formReducer = createReducer(
initialState,
on(formValueChange, (state, { type, ...update }) => ({ ...state, ...update }))
);
export function reducer(state: FormState | undefined, action: Action) {
return formReducer(state, action);
}
4단계(NgRx에만 해당)
Reducer를 Reducer/state 트리에 삽입합니다. 어디에 표시하든지 (참조 NgRx Docs.
5단계(NgRx)
양식이 포함된 어셈블리의 파일에 가져오기를 추가하려면 다음과 같이 하십시오.
import { using } from 'rxjs';
import { tap } from 'rxjs/operators';
import { formValueChange } from './form.actions';
component 클래스에 다음 코드를 추가합니다.
// this.form is the formGroup you created for the form
formValues$ = using(
() =>
this.form.valueChanges
.pipe(tap(values => this.store.dispatch(formValueChange(values))))
.subscribe(),
() => this.store.select(state => state.form) // Wherever you put it in your state tree
);
5단계(상태 적응)
양식이 포함된 어셈블리의 파일에 가져오기를 추가하려면 다음과 같이 하십시오.
import { toSource } from '@state-adapt/core';
import { initialState } from './form-state.interface';
component 클래스에 다음 코드를 추가합니다.
// this.form is the formGroup you created for the form
valueChanges$ = this.form.valueChanges.pipe(
toSource('[Form] Value Change'),
);
formValues$ = this.adapt.updater('form', initialState, this.valueChanges$);
6단계(NgRx 및 StateAdapt)
이 명령을 모듈에 넣습니다.
// patch-form-group-values.directive.ts
import { Directive, Input } from "@angular/core";
@Directive({
selector: "[patchFormGroupValues]"
})
export class PatchFormGroupValuesDirective {
@Input() formGroup: any;
@Input()
set patchFormGroupValues(val: any) {
if (!val) return;
this.formGroup.patchValue(val, { emitEvent: false });
}
}
7단계(NgRx 및 StateAdapt)
어셈블리 템플릿에서 새 명령을 사용하려면 다음과 같이 하십시오.
<form [formGroup]="form" [patchFormGroupValues]="formValues$ | async">
<input type="text" formControlName="name" />
<input type="number" formControlName="age" />
</form>
간이 복습법
다음은 NgRx 및 StateAdapt의 StackBlitz 작업 예입니다.Redux 개발 도구를 열고 양식을 편집할 때 봅니다.성공!
StateAdapt에는 2-4단계가 필요하지 않습니다.NgRx와 StateAdapt의 차이점을 보려면 다음과 같이 하십시오.
무엇이 그것을 이렇게 보잘것없게 만들었습니까?아무것도 아니에요.NgRx가 보유한 모든 계층을 소유합니다.층층이 얇아요.
여기서 나는 일반적인 this.adapt.updater
방법이 아니라 this.adapt.init
방법을 사용했다.이것은 문법 설탕으로 init
상태 변경 함수를 호출하는 데 사용되는 기본 어댑터입니다.나는 이것이 사기라고 생각하지 않는다. 왜냐하면 StateAdapt의 주요 장점 중 하나는 상태 관리 모델의 재사용을 장려하는 것이다. (NgRx/Entity의 생각에서 나온 것)그러나 문법적인 당분이 없어도 6줄 코드만 있으면 정의할 수 있다update
.
import { createAdapter } from '@state-adapt/core';
import { FormState } from './form-state.interface';
export const updateAdapter = createAdapter<FormState>()({
update: (state, update: Partial<FormState>) => ({...state, ...update}),
});
관심 있는 경우 StateAdapthere에 대한 자세한 내용을 참조하십시오.
선진적 방법
간단한 방법은 Redux 개발 도구에 하나의 작업 유형만 배치합니다.
만약 당신의 표가 매우 크다면, 당신은 좀 더 묘사적인 것을 원할 것이다.
기본 모드는 위의 간단한 방법에서 만들어졌기 때문에 확장하려면 updateAdapter
의 모든 속성에 대한 조작을 만들고, 모든 조작을 처리하기 위해 Reducer를 강화해야 합니다.만약 여러 개의 폼 그룹이 있다면, 각 폼 그룹에서 사용할 수 있다. FormState
단, 모든 폼 컨트롤에 대한 동작을 정의하려면 새로운 명령이 필요합니다.다음은 PatchFormGroupValues
명령을 사용할 수 있는 곳입니다.
// set-value.directive.ts
import { Directive, Input } from "@angular/core";
import { NgControl } from "@angular/forms";
@Directive({
selector: "[setValue]"
})
export class SetValueDirective {
@Input()
set setValue(val: any) {
this.ngControl.control.setValue(val, { emitEvent: false });
}
constructor(private ngControl: NgControl) {}
}
네가 상상한 바와 같이:
<form>
<input type="text" [formControl]="name" [setValue]="name$ | async" />
<input type="number" [formControl]="age" [setValue]="age$ | async" />
</form>
이 구성 요소에서, 각각의 폼 컨트롤 SetValue
을 감청하고, NgRx를 사용하면, 각각의 폼 컨트롤 valueChanges
을 호출합니다.나는 여기에 모든 코드를 붙이지 않지만, 나는 확실히 a working example in StackBlitz StateAdapt가 있다.결과적으로 현재 발생하고 있는 일에 대해 더욱 상세하게 알게 되었다.
다원
NgRx
using
는 단지 가능한 출처일 뿐이다.우리는 같은 방법으로 여러 개의 원본을 삽입할 수 있다.우리는 valueChanges
에서 그것들을 정의하지 않고 외부에서 그것들을 정의하여 RxJSusing
와 묶어서 구독을 받고 상점에 보낼 수 있도록 한다.
valueChanges$ = this.form.valueChanges.pipe(
tap(values => this.store.dispatch(formValueChange(values)))
);
delayedFormState$ = timer(5000).pipe(
tap(() =>
this.store.dispatch(delayedFormStateRecieved({ name: "Delayed", age: 1 }))
)
);
formValues$ = using(
() => merge(this.valueChanges$, this.delayedFormState$).subscribe(),
() => this.store.select(state => state.ngrx) // Wherever you put it in your state tree
);
merge
와 delayedFormStateRecieved
는 같지만 동작 유형은 다르다.나는 감속기를 확장해서 이 두 동작을 같은 방식으로 처리했다.
on(
formValueChange,
delayedFormStateRecieved,
(state, { type, ...update }) => ({ ...state, ...update })
)
주 적응
StateAdapt everywhere에서 소스를 삽입하거나 소스 그룹을 삽입할 수 있습니다.두 소스 모두 동일한 인터페이스를 사용하여 값을 보내고 동일한 상태 변경에 영향을 미치므로 여기서 하나의 배열을 사용합니다.
delayedFormState$ = timer(5000).pipe(
map(() => ({ name: "Delayed", age: 1 })),
toSource("[Form] Delayed Form State Received")
);
valueChanges$ = this.form.valueChanges.pipe(
toSource("[Form] Value Change")
);
formValues$ = this.adapt.updater("form", initialState, [
this.valueChanges$,
this.delayedFormState$
]);
융통성
이 다원적인 예는 함수식 반응식 프로그래밍의 유연성을 설명한다.정확한 가치를 발산하는 모든 원천을 삽입할 수 있다. 어디에서 오는지, 어떻게 사용할 계획인지는 신경 쓸 필요가 없다.이것은 코드를 바꾸지 않고 그 실현을 완전히 변경할 수 있다는 것을 의미한다.
이런 유연성은 표 상태의 모든 업무 논리가 함께 있다는 사실에서 나온다.이것은 jQuery, Angular Responsive Forms 및 다른 응용 프로그램의 명령 스타일보다 훨씬 유연하며, 각각의 이벤트 소스(또는 리셋 함수)는 응용 프로그램의 다른 영역에 대한 모든 의미를 정의해야 한다.명령식 프로그래밍은 관심점 분리를 위반하여 코드 실행 시간의 분리에 유리하다.응용 프로그램이 비동기적일수록 프로그래밍은 관심점 분리에 어긋난다.
결론
Redux 개발 도구가 폼을 쉽게 만들 때, 얼마나 많은 경우에 사용하고 싶지 않은지 알 수 없습니다.아마도 많은 폼에 대해 NgRx의 설정이 너무 많을 것이다. 그러나 만약 NgRx나 NGXS 프로젝트에 StateAdapt를 추가한다면, 정말로 4줄 정도의 코드만 추가하면 폼에 Redux 개발 도구를 사용할 수 있다.또한 앞으로 폼 상태를 관리할 때 당신은 더욱 반응성과 성명적인 기초를 가지게 될 것입니다!
같은 패턴을 따르면, 지령으로 폼 컨트롤의 다른 속성을 제어할 수도 있습니다.예를 들어, 나는 formValueChange
지령이 하나 있는데, 당신은 그것을 사용할 수 있습니다.
StateAdapt에 대한 자세한 내용은 read my introduction post 또는 visit the website로 문의하십시오.
Reference
이 문제에 관하여(왜 그리고 각 반응 형식의 상태를 어떻게 관리하는지), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/this-is-angular/why-and-how-to-manage-state-for-angular-reactive-forms-25fn
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
// form-state.interface.ts
export interface FormState { // Whatever it is
name: string;
age: number;
}
export const initialState: FormState = { name: '', age: 0 };
// form.actions.ts
import { createAction, props } from '@ngrx/store';
import { FormState } from './form-state.interface';
export const formValueChange = createAction(
'[Form] Value Change',
props<FormState>()
);
// form.reducer.ts
import { Action, createReducer, on } from "@ngrx/store";
import { FormState, initialState } from "./form-state.interface";
import { formValueChange } from "./form.actions";
const formReducer = createReducer(
initialState,
on(formValueChange, (state, { type, ...update }) => ({ ...state, ...update }))
);
export function reducer(state: FormState | undefined, action: Action) {
return formReducer(state, action);
}
import { using } from 'rxjs';
import { tap } from 'rxjs/operators';
import { formValueChange } from './form.actions';
// this.form is the formGroup you created for the form
formValues$ = using(
() =>
this.form.valueChanges
.pipe(tap(values => this.store.dispatch(formValueChange(values))))
.subscribe(),
() => this.store.select(state => state.form) // Wherever you put it in your state tree
);
import { toSource } from '@state-adapt/core';
import { initialState } from './form-state.interface';
// this.form is the formGroup you created for the form
valueChanges$ = this.form.valueChanges.pipe(
toSource('[Form] Value Change'),
);
formValues$ = this.adapt.updater('form', initialState, this.valueChanges$);
// patch-form-group-values.directive.ts
import { Directive, Input } from "@angular/core";
@Directive({
selector: "[patchFormGroupValues]"
})
export class PatchFormGroupValuesDirective {
@Input() formGroup: any;
@Input()
set patchFormGroupValues(val: any) {
if (!val) return;
this.formGroup.patchValue(val, { emitEvent: false });
}
}
<form [formGroup]="form" [patchFormGroupValues]="formValues$ | async">
<input type="text" formControlName="name" />
<input type="number" formControlName="age" />
</form>
import { createAdapter } from '@state-adapt/core';
import { FormState } from './form-state.interface';
export const updateAdapter = createAdapter<FormState>()({
update: (state, update: Partial<FormState>) => ({...state, ...update}),
});
// set-value.directive.ts
import { Directive, Input } from "@angular/core";
import { NgControl } from "@angular/forms";
@Directive({
selector: "[setValue]"
})
export class SetValueDirective {
@Input()
set setValue(val: any) {
this.ngControl.control.setValue(val, { emitEvent: false });
}
constructor(private ngControl: NgControl) {}
}
<form>
<input type="text" [formControl]="name" [setValue]="name$ | async" />
<input type="number" [formControl]="age" [setValue]="age$ | async" />
</form>
NgRx
using
는 단지 가능한 출처일 뿐이다.우리는 같은 방법으로 여러 개의 원본을 삽입할 수 있다.우리는 valueChanges
에서 그것들을 정의하지 않고 외부에서 그것들을 정의하여 RxJSusing
와 묶어서 구독을 받고 상점에 보낼 수 있도록 한다. valueChanges$ = this.form.valueChanges.pipe(
tap(values => this.store.dispatch(formValueChange(values)))
);
delayedFormState$ = timer(5000).pipe(
tap(() =>
this.store.dispatch(delayedFormStateRecieved({ name: "Delayed", age: 1 }))
)
);
formValues$ = using(
() => merge(this.valueChanges$, this.delayedFormState$).subscribe(),
() => this.store.select(state => state.ngrx) // Wherever you put it in your state tree
);
merge
와 delayedFormStateRecieved
는 같지만 동작 유형은 다르다.나는 감속기를 확장해서 이 두 동작을 같은 방식으로 처리했다. on(
formValueChange,
delayedFormStateRecieved,
(state, { type, ...update }) => ({ ...state, ...update })
)
주 적응
StateAdapt everywhere에서 소스를 삽입하거나 소스 그룹을 삽입할 수 있습니다.두 소스 모두 동일한 인터페이스를 사용하여 값을 보내고 동일한 상태 변경에 영향을 미치므로 여기서 하나의 배열을 사용합니다.
delayedFormState$ = timer(5000).pipe(
map(() => ({ name: "Delayed", age: 1 })),
toSource("[Form] Delayed Form State Received")
);
valueChanges$ = this.form.valueChanges.pipe(
toSource("[Form] Value Change")
);
formValues$ = this.adapt.updater("form", initialState, [
this.valueChanges$,
this.delayedFormState$
]);
융통성
이 다원적인 예는 함수식 반응식 프로그래밍의 유연성을 설명한다.정확한 가치를 발산하는 모든 원천을 삽입할 수 있다. 어디에서 오는지, 어떻게 사용할 계획인지는 신경 쓸 필요가 없다.이것은 코드를 바꾸지 않고 그 실현을 완전히 변경할 수 있다는 것을 의미한다.
이런 유연성은 표 상태의 모든 업무 논리가 함께 있다는 사실에서 나온다.이것은 jQuery, Angular Responsive Forms 및 다른 응용 프로그램의 명령 스타일보다 훨씬 유연하며, 각각의 이벤트 소스(또는 리셋 함수)는 응용 프로그램의 다른 영역에 대한 모든 의미를 정의해야 한다.명령식 프로그래밍은 관심점 분리를 위반하여 코드 실행 시간의 분리에 유리하다.응용 프로그램이 비동기적일수록 프로그래밍은 관심점 분리에 어긋난다.
결론
Redux 개발 도구가 폼을 쉽게 만들 때, 얼마나 많은 경우에 사용하고 싶지 않은지 알 수 없습니다.아마도 많은 폼에 대해 NgRx의 설정이 너무 많을 것이다. 그러나 만약 NgRx나 NGXS 프로젝트에 StateAdapt를 추가한다면, 정말로 4줄 정도의 코드만 추가하면 폼에 Redux 개발 도구를 사용할 수 있다.또한 앞으로 폼 상태를 관리할 때 당신은 더욱 반응성과 성명적인 기초를 가지게 될 것입니다!
같은 패턴을 따르면, 지령으로 폼 컨트롤의 다른 속성을 제어할 수도 있습니다.예를 들어, 나는 formValueChange
지령이 하나 있는데, 당신은 그것을 사용할 수 있습니다.
StateAdapt에 대한 자세한 내용은 read my introduction post 또는 visit the website로 문의하십시오.
Reference
이 문제에 관하여(왜 그리고 각 반응 형식의 상태를 어떻게 관리하는지), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/this-is-angular/why-and-how-to-manage-state-for-angular-reactive-forms-25fn
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(왜 그리고 각 반응 형식의 상태를 어떻게 관리하는지), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/this-is-angular/why-and-how-to-manage-state-for-angular-reactive-forms-25fn텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)