Angular: 반응형 형식은 강력한 형식이 아닙니다.

에 만든 2016년 12월 30일  ·  90코멘트  ·  출처: angular/angular

[x] feature request
  • 각도 버전: 2

반응형 양식은 복잡한 양식에서 사용하기 위한 것이지만 컨트롤의 valueChangesObservable<any> 이며 이는 복잡한 코드에 대한 모범 사례에 완전히 반대됩니다.

강력한 형식의 양식 컨트롤을 만드는 방법이 있어야 합니다.

forms feature high

가장 유용한 댓글

안녕하세요, 저는 Angular 팀의 업데이트를 공유하고 싶었습니다. 이것이 큰 골칫거리라고 들었습니다. 우리는 기존 PR을 살펴보고 모든 의견을 다시 검토하는 것을 포함하여 더 강력한 형식의 양식에 대한 작업을 곧 시작할 것입니다. 시간을 내어 의견을 남겨주신 모든 분들께 감사드립니다!

모든 90 댓글

관련 #11279

이것은 #11279와 관련이 없습니다.

관련이 없는지 설명해주세요.
Abstract Control이 Generic이 되기를 원하는가? 이것이 valueChanges가 Observable<any> 가 아닌 유형을 가질 수 있는 유일한 방법이며 유형을 유추하는 다른 방법은 없습니다.
이것은 #5404가 묻는 것입니다. 이것은 #11279와 관련이 있음을 의미합니다.
AbstractControl을 일반화하지 않고 구현할 수 있는 다른 방법이 있다면 설명해주십시오.

#11279에서와 같이 get<Type> 를 사용하는 것은 확실히 잘못된 해결책입니다. 타이프 라이터는, 자바 무제한으로 와일드 카드처럼 somethign 한 경우 get 그것을 사용하지 것이다 any . 빈 인터페이스로 같은 방식으로 뭔가를 할 수 있을까요?

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html keyof 있습니다. TypeScript 2.1 기능은 강력한 형식의 양식 컨트롤을 구현하기 위해 연구하는 데 매우 흥미로울 수 있습니다.

현재 방식으로는 불행히도 큰 앱에는 사용할 수 없으며 그 위에 무언가를 디자인해야합니다.

방금 TS 2.2(https://github.com/Microsoft/TypeScript/wiki/Roadmap#22-february-2017)에서 기본 일반 유형(https://github.com/Microsoft/TypeScript/)을 계획한 것으로 나타났습니다. Issues/2175) 일단 우리가 이것을 가지고 있으면 AbstractControl AbstractControl<T = any> 와 같이 일반화할 수 있으므로 이 문제를 다시 방문하는 것이 좋습니다. 여기서 T 는 다음 유형입니다. valueChanges 의해 반환된 값은 Observable<T> 입니다. 대규모 브레이킹 체인지이기 때문에 현재 하는 것은 좋은 생각이 아니지만 기본 제네릭을 사용하면 제가 잘못 이해하지 않는 한 브레이킹 체인지가 되지 않습니다.

이에 대한 작은 업데이트는 기본 제네릭이 TS2.3 으로 이동된

기본 제네릭 유형이 있는 TypeScript 2.3이 이미 있습니다. 앵귤러에서 TS 2.3에 대한 지원이 준비될 때 어떤 계획이 있습니까?

@desfero 는 빌드가 TS2.3으로 업그레이드될 때까지 #16707을 기다리고 있습니다.

+1은 이 기능을 보고 싶어합니다. 작업 중인 사람이 있습니까?

이것은 유용할 수 있습니다 - Angular Typesafe Reactive Forms

이에 대한 작은 업데이트:
여기 내 의견에 따라 : https://github.com/angular/angular/pull/16828#issuecomment -337034655
현재 Forms API에 제네릭을 구현하는 것은 변경을 중단하지 않고는 불가능합니다.
따라서 주요 변경 사항이 필요합니다.
또는 전체 양식 재작성

그래서 내 이전 댓글이 잘못된 것으로 판명되었습니다.
여기 #20040에서 볼 수 있듯이 현재 Forms API에서 이것을 구현할 수 있었습니다.

@Toxicable 은 여전히 ​​안전하게 리팩토링할 수 있는 기능이 부족하다는 문제가 있습니다. 예를 들어 get('person')은 실제로 기호 자체를 사용하지 않습니다. @rpbeukes 에서

@howiempt

예를 들어 get('person')은 실제로 기호 자체를 사용하지 않습니다.

이게 무슨 말인지 모르겠는데 여기서 무슨 상징을 말씀하시는 겁니까?
내 구현에서 다음과 같이 할 수 있습니다.

let g = new FormGroup({
  'name': new FormControl('Toxicable'),
  'age': new FormControl(22),
})

g.get('name') //AbstractControl<string>
g.get('age') //AbstractControl<number>

문자열을 사용하지 않고 get(obj.person)

이것은 여러 FormGroup을 순회하는 기능이 부족합니다.
내 방법은 이 시나리오에서 유형을 유추할 수 없지만 내 PR의 아이디어는 변경 사항을 중단하거나 새로운 API(제네릭 제외)를 도입하지 않고 일반 유형을 추가하는 것입니다.

@Toxicable 나는 당신의 변화가 문제를 깨뜨리지 않고 당신의 솔루션을 비판하려는 것이 아니라는 것을 이해합니다. 다른 구현(개조)에서는 문자열이 아닌 실제 속성을 사용할 수 있습니다. 문자열로 필드를 참조하여 해당 속성 이름이 변경되면 나에게 매우 안전하지 않은 중단이 빌드됩니다. 예를 들어, 필드 이름을 'name'에서 'firstName'으로 변경하면 모든 g.get('name') 참조를 변경하지 않으면 중단됩니다. 내가 할 수 있다면

class PersonDetails {
  name: string;
  age: number;
}
let g = new FormGroup<PersonDetails>({
  name: new FormControl('Toxicable'),
  age: new FormControl(22),
})

g.get(name) //AbstractControl<string>
g.get(age) //AbstractControl<number>

그들은 모두 긴밀한 참조가 될 것입니다. 개조 솔루션은 약간 해킹된 방식으로 이를 수행하지만 해당 문제도 해결합니다.

@Toxicable 홍보

나는 @howiempt에 동의합니다. 만약 우리가 이와 같은 것을 얻을 수 있다면 그것은 일등상일 것입니다:

g.get(x => x.name) //AbstractControl

다시 말하지만, 이것이 더 큰 범위 내에서 얼마나 실현 가능한지 모르겠습니다.
당신의 판단을 믿습니다.

좋은 일을 계속하고 빠른 응답에 감사드립니다.

다른 컨트롤에 액세스하는 이 방법은 제네릭 추가와 관련이 없다고 생각합니다.
그러나 그것에 대해 다른 문제를 자유롭게 여십시오.

반환 유형을 설정하는 것이 실제로 "강력한 유형"이라고 생각하지 않고 구현의 절반이 필요한 것처럼 보이지만 올바른 방향으로 나아가는 단계입니다.

안녕하세요, 이 문제를 해결하기 위해 https://github.com/Quramy/ngx-typed-forms 를 출시했습니다. 꼭 확인하세요 😄

@Quramy 몇 주 전에 귀하의 패키지를 사용하려고 시도했는데 제 기억으로는 실제로 많은 시행을 하지 않습니다 :(

+1. 구현되기를 원했는데 인스턴스 수를 셀 수 없습니다.

같은.
Angular Reactive 형식은 정말 다른 어떤 프레임워크도 능가하는 기능 중 하나입니다. Reactive 형식을 강력한 형식으로 만들면 다음 단계로 이동하여 경쟁과의 격차를 더욱 넓힐 수 있습니다. :)

이것은 조건부 매핑 유형 및 재귀로 수행할 수 있습니다..... 조건부 매핑 유형은 방금 typescript에 병합되었습니다. 이것이 게시되면 강력한 형식의 양식을 가능하게 할 기회가 있습니다.

기다릴 수 없어(

@Quramy 솔루션은 슬프게도 FormBuilder 대한 모든 인수의 구조를 적용하지 않습니다. 또한 일반 FormGroup<T> , FormControll<T>FormArray<T>AbtractControl<T> 이벤트를 확장하지 않는 인터페이스일 뿐이므로 직접 사용할 수 없습니다. 이것은 현재 프로젝트에 충분하지 않았습니다.

ngx-strongly-typed-forms를 사용 하여 이제 강력한 형식의 양식 프로젝트를 직접 릴리스했습니다.
기본 제네릭을 사용하지 않기 때문에 하위 호환성으로 약간 중단됩니다. 따라서 컨트롤에 any 유형을 명시적으로 부여해야 하지만 다른 모든 부분에 더 많은 유형 안전성을 추가하고 현재 @angular/forms 구현과 API 호환이 가능합니다.
아마도 이것은 이 기능이 Angular에서 구현될 때까지 유효한 대안일 것입니다.

+1 이것은 강력한 기능입니다..

조속히 시행해야 함)

코딩 방식

이 오픈 소스 소프트웨어(AGPL)에는 흥미로운 구현이 있습니다.

https://github.com/concentricsky/badgr-ui/blob/master/src/app/common/util/typed-forms.ts

임시 해결 방법으로 이것을 사용했습니다.
도움이 되기를 바랍니다.

import { FormControl, AbstractControl } from "@angular/forms";
export class NumberControl extends FormControl{
    _value: Number;
    get value(){ 
        return this._value;
    }
    set value(value){
        this._value = Number(value);
    }
}

이것은 내가 작업한 프로젝트에서 몇 번 생각해 보았지만 이러한 값을 관찰하는 모든 항목에 미칠 성능 영향을 알 만큼 JavaScript 프록시를 사용하지 않았습니다.

FormBuilder 수준에서 사용자 지정 해결 방법을 만들었습니다.

import { Injectable } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
type ExtraProperties = { [key: string]: any } | null;
interface IModelConstructor { new(...args: any[]): any }

@Injectable({
    providedIn: 'root'
})
export class TypedFormBuilder {
    array = this.formBuilder.array;
    control = this.formBuilder.control;
    group = this.formBuilder.group;

    constructor(private formBuilder: FormBuilder) {}

    typedGroup<TGroupModel extends object>(ModelType: IModelConstructor, extraProps?: ExtraProperties): FormGroup & TGroupModel {
        const formGroup: any = this.group({
            ...new ModelType(),
            ...extraProps
        });

        return new Proxy<FormGroup & TGroupModel>(formGroup, {
            get: (target: FormGroup & TGroupModel, propName: string | number | symbol) => {
                if (propName in target) {
                    return target[propName];
                } else {
                    const property = target.get(propName as string);
                    return property && property.value;
                }
            },
            set: (target: FormGroup & TGroupModel, propName: string | number | symbol, value: any, _: any) => {
                if (propName in target) {
                    target[propName] = value;
                } else {
                    target.setValue({ [propName]: value });
                }

                return true;
            }
        });
    }
}

이 솔루션은 확실히 세련되지 않으며 FormGroup에서 생성된 속성 값에 액세스하는 것이 가장 좋습니다(예: "필드"가 제공된 유형임). 그러나 이것은 강력한 타이핑을 제공합니다. 그것을 사용하려면:

generateFormGroup() {
    this.theGroup = builder.typedGroup<MyModel>(MyModel);
    this.theGroup.someProperty = 5;
}

저는 지난 몇 달 동안 현재 프로젝트에서 양식 타이핑 을 사용하여 타이핑된 양식에 대한 경험을 수집했습니다. 3개의 개발자 팀과 함께 하나의 프로젝트에서 작업할 때 큰 가치를 제공하며 모두가 전환하는 것은 비용이 정말 저렴하기 때문입니다.

그러나 현재 API에 제네릭을 넣는 것이 올바른 결정인지 논의하고 싶습니다. 모든 양식 컨트롤에 대한 유형을 구축하는 동안 나는 정적 유형이 그 시점에서 가능하지 않았으므로 가장 큰 우려 중 하나가 아니라고 생각하기 때문에 유형이 불가능하거나 다루기 힘든 많은 경우를 발견했습니다.
슬프게도 이것은 AbstractControl#value 있는 주요 기능을 대상으로 하며, 이는 DeepPartial<T> 또는 각 하위 클래스에 대해 서로 다른 구현이 있는 AbstractControl#get 여야 합니다.
이전 버전과의 호환성은 any 유형의 케이스를 통해 넘어짐으로 인해 일부 유형 안전성을 잃게 됩니다.
반응형 양식에 대한 새로운 API를 고려하는 것도 이 문제에 대한 옵션입니까?

그래서 이것이 실제 솔루션이 발생하는 동안 내가 한 일입니다.

면책 조항 ... 방금 Angular에서 시작했지만 Typescript에 매우 익숙하므로 반응 형식을 완전히 이해하지 못합니다 ... 여기에서 결과적으로 저에게 효과가 있었지만 물론 완전히 완료되지는 않았습니다. 방금 FormGroup을 입력했지만 양식에 대해 더 배우면서 더 많은 것을 입력해야 할 것입니다...

import { FormGroup } from '@angular/forms';

export class FormGroupTyped<T> extends FormGroup {
  public get value(): T {
    return this.value;
  }
}

그런 다음 이렇게 사용할 수 있습니다.

import { FormGroupTyped } from 'path/to/your/form-group-typed.model';

interface IAuthForm {
  email: string;
  password: string;
}

const authForm: FormGroupTyped<IAuthForm> = fb.group({
  email: ['', [Validators.required]],
  password: ['', [Validators.required]],
});

const formValues: IAuthForm = this.authForm.value;
const email: string = formValues.email; 
const invalidKeyVar: string = formValues.badBoy; // [ts] Property 'badBoy' does not exist on type 'IAuthForm'. [2339]
const invalidTypeVar: number = formValues.password; // [ts] Type 'string' is not assignable to type 'number'. [2322]

@Toxicable Lol이 이 정확한 문제를 찾고 있었습니다! ㅋ

@cafesanu 다음은 생성자를 확인하기 위해 입력한 FormGroup을 약간 개선한 것입니다.

import { AbstractControl, AbstractControlOptions, AsyncValidatorFn, FormGroup, ValidatorFn } from '@angular/forms';

export class FormGroupTyped<T> extends FormGroup {
  readonly value: T;
  readonly valueChanges: Observable<T>;

  constructor(controls: { [key in keyof T]: AbstractControl; },
              validatorOrOpts?: ValidatorFn | Array<ValidatorFn> | AbstractControlOptions | null,
              asyncValidator?: AsyncValidatorFn | Array<AsyncValidatorFn> | null) {
    super(controls, validatorOrOpts, asyncValidator);
  }

  patchValue(value: Partial<T> | T, options?: {
    onlySelf?: boolean;
    emitEvent?: boolean;
  }): void {
    super.patchValue(value, options);
  }

  get(path: Array<Extract<keyof T, string>> | Extract<keyof T, string>): AbstractControl | never {
    return super.get(path);
  }
}

용법 :

export class SearchModel {
  criteria: string;
}

const searchForm = new FormGroupTyped<SearchModel >({
  criteria: new FormControl('', Validators.required) // OK
  badBoy: new FormControl('', Validators.required)  // TS2345: Object literal may only specify known properties, and 'badBoy' does not exist in type '{ criteria: AbstractControl; }'.
});

빌더에 대한 호출을 기반으로 데이터 유형을 동적으로 생성할 수 있는 FromControl 주위에 작은 래퍼를 작성했습니다. https://github.com/concentricsky/badgr-ui/blob/develop/src/app/common/util/ typed-forms.ts

동적으로 생성된 유형은 인터페이스 유형을 미리 선언한 다음 양식을 만들기 위해 올바른 호출을 할 필요 없이 양식의 모양이 예상과 일치하는지 확인합니다.

언젠가 Angular에서 이와 비슷한 것을 보고 싶습니다.

사용법은 다음과 같습니다.

// Create a typed form whose type is dynamically constructed by the builder calls
const group = new TypedFormGroup()
    .add("firstName", typedControl("", Validators.required))
    .add("lastName", typedControl("", Validators.required))
    .add(
        "address",
        typedGroup()
            .add("street", typedControl("2557 Kincaid"))
            .add("city", typedControl("Eugene"))
            .add("zip", typedControl("97405"))
    )
    .addArray(
        "items",
        typedGroup()
            .addControl("itemName", "")
            .addControl("itemId", 0)
    )
;

// All these are type checked:
group.value.address.street.trim();
group.controls.firstName.value;
group.untypedControls.firstName.value;
group.value.items[0].itemId;

안녕하세요 지난 3일 동안 d.ts gist 를 사용하여 원래 각도 클래스와 호환되는 새로운 Typed 인터페이스를 생성하는 ReactiveForms 클래스에 대한 보다 엄격한 정의를 정의하는 실험을 했습니다.
나는 그것이 당신의 문제에 대한 가능한 해결책/해결 방법이 될 수 있다고 생각합니다 😉

//BASIC TYPES DEFINED IN @angular/forms + rxjs/Observable
type FormGroup = import("@angular/forms").FormGroup;
type FormArray = import("@angular/forms").FormArray;
type FormControl = import("@angular/forms").FormControl;
type AbstractControl = import("@angular/forms").AbstractControl;
type Observable<T> = import("rxjs").Observable<T>;

type STATUS = "VALID" | "INVALID" | "PENDING" | "DISABLED"; //<- I don't know why Angular Team doesn't define it https://github.com/angular/angular/blob/7.2.7/packages/forms/src/model.ts#L15-L45)
type STATUSs = STATUS | string; //<- string is added only becouse Angular base class use string insted of union type https://github.com/angular/angular/blob/7.2.7/packages/forms/src/model.ts#L196)

//OVVERRIDE TYPES WITH STRICT TYPED INTERFACES + SOME TYPE TRICKS TO COMPOSE INTERFACE (https://github.com/Microsoft/TypeScript/issues/16936)
interface AbstractControlTyped<T> extends AbstractControl {
  // BASE PROPS AND METHODS COMMON TO ALL FormControl/FormGroup/FormArray
  readonly value: T;
  valueChanges: Observable<T>;
  readonly status: STATUSs;
  statusChanges: Observable<STATUS>;
  get<V = unknown>(path: Array<string | number> | string): AbstractControlTyped<V> | null;
  setValue<V>(value: V extends T ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  patchValue<V>(value: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  reset<V>(value?: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
}

interface FormControlTyped<T> extends FormControl {
  // COPIED FROM AbstractControlTyped<T> BECOUSE TS NOT SUPPORT MULPILE extends FormControl, AbstractControlTyped<T>
  readonly value: T;
  valueChanges: Observable<T>;
  readonly status: STATUSs;
  statusChanges: Observable<STATUS>;
  get<V = unknown>(path: Array<string | number> | string): AbstractControlTyped<V> | null;
  setValue<V>(value: V extends T ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  patchValue<V>(value: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  reset<V>(value?: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
}
interface FormGroupTyped<T> extends FormGroup {
  // PROPS AND METHODS SPECIFIC OF FormGroup
  //controls: { [P in keyof T | string]: AbstractControlTyped<P extends keyof T ? T[P] : any> };
  controls: { [P in keyof T]: AbstractControlTyped<T[P]> };
  registerControl<P extends keyof T>(name: P, control: AbstractControlTyped<T[P]>): AbstractControlTyped<T[P]>;
  registerControl<V = any>(name: string, control: AbstractControlTyped<V>): AbstractControlTyped<V>;
  addControl<P extends keyof T>(name: P, control: AbstractControlTyped<T[P]>): void;
  addControl<V = any>(name: string, control: AbstractControlTyped<V>): void;
  removeControl(name: keyof T): void;
  removeControl(name: string): void;
  setControl<P extends keyof T>(name: P, control: AbstractControlTyped<T[P]>): void;
  setControl<V = any>(name: string, control: AbstractControlTyped<V>): void;
  contains(name: keyof T): boolean;
  contains(name: string): boolean;
  get<P extends keyof T>(path: P): AbstractControlTyped<T[P]>;
  getRawValue(): T & { [disabledProp in string | number]: any };
  // COPIED FROM AbstractControlTyped<T> BECOUSE TS NOT SUPPORT MULPILE extends FormGroup, AbstractControlTyped<T>
  readonly value: T;
  valueChanges: Observable<T>;
  readonly status: STATUSs;
  statusChanges: Observable<STATUS>;
  get<V = unknown>(path: Array<string | number> | string): AbstractControlTyped<V> | null;
  setValue<V>(value: V extends T ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  patchValue<V>(value: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  reset<V>(value?: V extends Partial<T> ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
}

interface FormArrayTyped<T> extends FormArray {
  // PROPS AND METHODS SPECIFIC OF FormGroup
  controls: AbstractControlTyped<T>[];
  at(index: number): AbstractControlTyped<T>;
  push<V = T>(ctrl: AbstractControlTyped<V>): void;
  insert<V = T>(index: number, control: AbstractControlTyped<V>): void;
  setControl<V = T>(index: number, control: AbstractControlTyped<V>): void;
  getRawValue(): T[];
  // COPIED FROM AbstractControlTyped<T[]> BECOUSE TS NOT SUPPORT MULPILE extends FormArray, AbastractControlTyped<T[]>
  readonly value: T[];
  valueChanges: Observable<T[]>;
  readonly status: STATUSs;
  statusChanges: Observable<STATUS>;
  get<V = unknown>(path: Array<string | number> | string): AbstractControlTyped<V> | null;
  setValue<V>(value: V extends T[] ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  patchValue<V>(value: V extends Partial<T>[] ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
  reset<V>(value?: V extends Partial<T>[] ? V : never, options?: { onlySelf?: boolean; emitEvent?: boolean }): void;
}

stackblitz 에서 사용 테스트 - 코드를 다운로드하고 로컬 VSCode에서 실행하십시오. stackblitz에서 setValue 및 pathValue에 대한 오류가 올바르지 않은 이유를 모르겠습니다...

트위터 스레드 에서 @IgorMinar 와 몇 가지 "미래 아이디어"(V8+ 이후)에 대해 논의합니다.

모든 의견, 제안 및 도움을 매우 환영합니다 !

내 솔루션은 @ng-stack/forms 입니다. 흥미로운 기능 중 하나는 양식 유형의 자동 감지입니다.

따라서 구성 요소에서 다음을 수행할 필요가 없습니다.

get userName() {
  return this.formGroup.get('userName') as FormControl;
}

get addresses() {
  return this.formGroup.get('addresses') as FormGroup;
}

이제 다음을 수행하십시오.

// Note here form model UserForm
formGroup: FormGroup<UserForm>;

get userName() {
  return this.formGroup.get('userName');
}

get addresses() {
  return this.formGroup.get('addresses');
}

자세한 내용은 @ng-stack/forms를 참조하세요.

안녕하세요, 우리는 최근 두 가지에 대해 @zakhenry 와 협력하고 있습니다.

  • 양식 유형 개선
  • 하위 양식 관리 방법 개선(하위 구성 요소 내)

@dmorosinotto 처럼 모든 유형을

누구든지 lib를 살펴보고 싶다면: ngx-sub-form

안녕하세요 @maxime1992 Controls<T> 보았지만 AbstractControl to <T[K]> 엄격히 준수하지 않습니다.
이유를 여쭤봐도 될까요? FormControl 및 관련 유형을 재정의하고 변경하기 위해 setControl 또는 registerControl 메소드와 같은 것을 사용하여 런타임에 유형을 변경해야 한다고 느끼기 때문에 "유형화되지 않은" 상태로 두십니까?
내 TypedForms.d.ts가 약간 더 엄격하고 AbstractControlTyped to the type T<P> "강제"하지만 이러한 종류의 선택으로 원래 ReactiveForms API에서 허용되고 사용되는 것을 강제/비활성화하는지 모르겠습니다. 누구...

어떤 생각? 고려해야 할 실제 사례가 있습니까?
이에 대한 모든 의견은 내가 만든 정의와 작업 중인 PR을 변경하는 방법을 결정하는 데 도움이 될 수 있습니다.
감사 해요

추신: ngx-sub-form에 대한 훌륭한 작업 👍 ControlValueAccessor를 사용하여 하위 양식을 처리하는 아이디어는 저도 실험 중이었습니다 😉

@ng-stack/forms의 경우 지원 유형 유효성 검사가 추가되었습니다.

클래스 FormControl , FormGroup , FormArrayFormBuilder 의 모든 메서드
제네릭에 대한 두 번째 매개변수로 "오류 유효성 검사 모델"을 수락합니다.

class ValidationModel {
  someErrorCode: { returnedValue: 123 };
}
const control = new FormControl<string, ValidationModel>('some value');
control.getError('someErrorCode'); // OK
control.errors.someErrorCode; // OK
control.getError('notExistingErrorCode'); // Error: Argument of type '"notExistingErrorCode"' is not...
control.errors.notExistingErrorCode; // Error: Property 'notExistingErrorCode' does not exist...

기본적으로 ValidatorsModel 라는 특수 유형이 사용되었습니다.

const control = new FormControl('some value');
control.getError('required'); // OK
control.getError('email'); // OK
control.errors.required // OK
control.errors.email // OK
control.getError('notExistingErrorCode'); // Error: Argument of type '"notExistingErrorCode"' is not...
control.errors.notExistingErrorCode // Error: Property 'notExistingErrorCode' does not exist...

ValidatorsModel 에는 typeof Validators 에서 추출한 속성 목록과 예상 반환 유형이 포함됩니다.

class ValidatorsModel {
  min: { min: { min: number; actual: number } };
  max: { max: { max: number; actual: number } };
  required: { required: true };
  requiredTrue: { required: true };
  email: { email: true };
  minLength: { minlength: { requiredLength: number; actualLength: number } };
  maxLength: { requiredLength: number; actualLength: number };
  pattern: { requiredPattern: string; actualValue: string };
}

@dmorosinotto 정말 감사
Validators.compose([Validators.required, Validators.maxLength(20), Validators.minLength(3)])

안녕하세요 @youssefsharief Validators 문제에 대해 자세히

샘플 코드나 stackblitz를 게시할 수 있다면 제가 지켜보고 해결책을 찾으면 문제를 해결하는 데 도움을 주도록 노력하겠습니다 😉

@ng-stack/forms 경우 input[type="file"] 대한 지원이 추가되었습니다.

stackblitz의 예도 참조하십시오.

또한 FormData 를 사용하여 파일을 업로드하는 "클래식" 접근 방식을 사용했지만 몇 가지 이익이 있습니다.

  • 자동으로 양식 입력 이름 검색, 예: <input type="file" name="someName"> -> formControl.value with someName 필드 이름;
  • 필드 이름을 올바르게 설정하여 multiple 속성 지원(여기에서 userpic[] 참고)
<input type="file" multiple name="userpic" [formControl]="formControl">

```
formData.append('userpic[]', myFileInput.files[0]);
formData.append('userpic[]', myFileInput.files[1]);

- `Validators` have four static methods for files
```ts
import { Validators } from '@ng-stack/forms';

Validators.fileRequired;
Validators.fileMaxSize(1024);
Validators.filesMaxLength(10);
Validators.filesMinLength(2);

@KostyaTretyak 지금까지 본 패키지는 Angular Reactive Forms의 현재 입력 문제에 대한 훌륭한 솔루션인 것 같습니다. 직접 구현하는 대신 Angular 자체에 기여하는 것에 대해 생각해 본 적이 있습니까?

우리는 거의 Angular 8에 있으며 100% TypeScript 환경에서 즉시 사용 가능한 형식이 없습니다.

@kroeder , 현재 이슈가 생성된 지 2년이 비슷한 Pull Request 가 1.5년 전에 병합 없이 생성된 것을 알 수 있습니다.

그러나 @ng-stack/forms 가 인기가 있고 Angular 8+와 호환된다면 아마도 앞으로 Pull Request를 만들 것입니다.

@KostyaTretyak 소리가 좋습니다 :) 저는 이 스레드의 앞부분에서 언급하고 @no0x9d가 만든 https://github.com/no0x9d/ngx-strongly-typed-forms 를 사용하고 있습니다. 라이브러리는 그것과 어떻게 다릅니까?

@ZNS , 잘 모르겠지만 빠른 검토 후에 ngx-strongly-typed-forms 에 유효성 검사를 위한 제네릭이 없다고 생각하고 자동으로 양식 컨트롤에 적절한 유형을 감지합니다 . 내가 틀렸을 수도 있습니다.

@ZNS @KostyaTretyak 안녕하세요. 위에서 언급했듯이 저는 ngx-strongly-typed-forms 의 저자입니다.
@ng-stack/forms 의 기능 세트를 간략히 검토한 결과 약간의 차이점이 있다고 생각합니다.

거의 모든 솔루션 또는 해결 방법과 내 프로젝트 사이에는 개념적 차이가 있습니다.
대부분의 경우 원래 Angular FormControl은 일부 프록시에 의해 확장되거나 래핑됩니다. 일부 메서드는 다른 형식 서명으로 재정의되고 원래 함수에 위임됩니다.
이것은 성능에 미미한 영향을 미치는 새 레이어를 도입하지만 더 중요한 것은 유지 관리해야 하고 프로젝트에 버그를 유발할 수 있는 코드입니다.
제 생각에는 이것은 컴파일 타임의 정적 검사에 필요하지 않습니다. 유일한 런타임 부분은 내가 입력한 FormBuilder를 제공하는 NgModule이며, 실제로는 원래 Angular FormBuilder입니다. 다른 모든 것은 Angular 코드일 뿐입니다.

직접 비교에서 나는 ValidationModel과 개체에서 FormGroups로, Arrays에서 FormArrays로의 변환을 가지고 있지 않지만 AbstractControl#get 를 좀 더 형식적으로 안전하게 만들고 유효성 검사기 인수를 입력하도록 일부 의견을 변경했습니다.

몇 가지 작은 추가 사항으로 내 코드는 이전 버전과 호환될 수 있으며 pull 요청을 생성할 수 있습니다. 그러나 유사한 pull 요청은 오랫동안 유효하지 않습니다.
그러나 Angular에서 이것을 얻으려는 노력이 있다면 기꺼이 힘을 합칠 것입니다. 다른 한편으로는 엄격하게 입력하도록 더 잘 설계된 양식용 새 API를 보고 싶습니다. 자세한 내용은 내 의견 을 참조하십시오.

+1은 이 기능을 보고 싶어합니다. 작업 중인 사람이 있습니까?

앵귤러 팀에 충돌?

그들은 개발자에 대해 관심이 없습니다. 그들은 블랙잭과 굉장한 5%-번들 크기 감소 기능과 함께 자신의 백로그를 가지고 있습니다.

@Lonli-Lokli

그들은 개발자에 대해 관심이 없습니다.

비판은 쉽습니다. 다른 개발자에 대해 더 관심이 있습니까?
양식을 개선하기 위한 PR이나 앞으로 나아갈 수 있도록 하는 건설적인 의견이나 RFC를 본 적이 없습니다.

그들만의 백로그가 있다

안 돼:두려워:!
사람들은 회사가 _[누가 비용을 지불하는지]_ 필요로 하는 것을 우선시하고 있습니까?
부끄러운 일이야!
image

멋진-5%-번들 크기 감소 기능.

Ivy와 (현재) 번들 크기의 아주 작은 차이에 대해 분명히 이야기하고 있습니다.
Ivy는 현재 실험적이며 선택해야 합니다. 아직 완벽하지 않다는 사실이 놀랍습니다! :생각:
예, Ivy는 번들 크기를 줄이는 데 도움이 되고 도구가 앱에서 더 나은 트리 쉐이킹을 수행할 수 있도록 한다고 들었습니다. 그리고 바라건대, 그것이 올 것입니다! 지금은 아무 것도 깨지지 않는지 확인하는 작업만 하고 있으며 나중에 더 나은 디버깅 정보, 모듈 기반이 아닌 구성 요소 기반의 증분 컴파일 및 트리 쉐이킹을 제공하는 데 도움이 될 수 있습니다. 그러나 나무를 흔드는 도구는 나중에 작동합니다.

따라서 오픈 소스 프레임워크를 무료로 제공하는 사람들을 존중하고 괴롭히지 않도록 하십시오. 모든 것이 완벽하지 않고 거대한 작업이 진행 중입니다. 예, 일부 문제가 남아 있는 것처럼 느껴지지만 그 리팩토링이 필요했으며 그렇게 큰 것을 만들기에 좋은 시간은 결코 없을 것입니다.

비생산적인 것에 대해 이야기하는 이 스레드를 독점하고 싶지 않기 때문에 이제 이 논쟁에서 벗어났습니다. *날아 가다*

@maxime-allex
다른 PR도 많이 있는데(현재 386개), 하나 더 있으면 뭔가 바뀔 것 같나?
이 문제에 대해 말하면 이 관련 PR(https://github.com/angular/angular/pull/20040)이 아직 병합되지 않았습니다.

리팩토링에 대해 말하자면 Ivy는 1년 전에 언급되었습니다. 누군가 처리할 수 있는 것이 개발자의 주요 기능이라는 것을 알고 있지만 개인적으로 가능한 한 많은 개발자에게 중요한 수정 사항을 확인하는 것을 선호합니다.
https://github.com/angular/angular/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc

나는 여전히 Angular가 마케팅을 위한 것이 아니라 개발자를 위한 것이므로 커뮤니티의 반응에 따라 더 많은 문제가 해결되기를 바랍니다. 이것이 제 질문입니다. 여기서 우선순위는 무엇입니까?

분명히 이 문제는 기존 API에 대한 업데이트를 통해 해결할 수 있지만, 별도로 저는 ReactiveFormsModule을 개선하여 이 문제를 포함하여 많은 미해결 문제를 해결하기 위한 제안을 만들었습니다.

해결된 다른 문제로는 모든 속성에 대한 업데이트를 구독하는 기능과 서비스를 통해 컨트롤을 비동기식으로 검증하는 기능이 있습니다( asyncValidator ). 자세한 내용은 #31963에서 확인할 수 있습니다. 피드백을 환영합니다!

전에 약속한 대로 풀 리퀘스트를 만들었습니다. 이 PR에는 @ng-stack/forms 기능의 일부만 포함되어 있습니다(확인 제외, 양식 제어 자동 감지 및 입력[파일] 지원).

안녕하세요, 저는 Angular 팀의 업데이트를 공유하고 싶었습니다. 이것이 큰 골칫거리라고 들었습니다. 우리는 기존 PR을 살펴보고 모든 의견을 다시 검토하는 것을 포함하여 더 강력한 형식의 양식에 대한 작업을 곧 시작할 것입니다. 시간을 내어 의견을 남겨주신 모든 분들께 감사드립니다!

오!!!!! 종료!

그것은 아주 좋은 소식입니다, Angular 팀, 감사합니다!
릴리스가 나오자마자 angular-typesafe-reactive-forms-helper를 더 이상 사용하지 않을 것입니다.

예스!!!!

너무 기대된다!! Angular 팀 감사합니다!!

스팸 반응을 멈출 수 있습니까?
이모티콘은 https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/ 용이므로 반응에 사용

Angular 팀이 강력한 형식의 Reactive Form 작업을 확인했기 때문입니다. 부드러운 정적 유형 개발 경험을 얻기 위해 값 유형을 추출하기 위해 infer 유형

첫 번째 접근: 값 유형으로 시작

FormGroup을 디자인하기 시작했을 때, 직관적인 단순 값 유형을 T .

FormGroup<{ id: string, username: string | null }>

그러나 복잡한 테이블 형식 아키텍처를 처리해야 할 때 Angular HTML에서 형식 안전 바인딩을 수행하는 것이 매우 어렵다는 것을 알았습니다.

type User = { id: string, username: string | null }
const table: FormArray<User> = new FormArray([{ id: '0', username: 'gaplo917'}])

const row: FormGroup<User> = table.at(0) as FormGroup<User> // you must do a type cast

// NO way to get type-safe binding in Angular HTML
<input [formControl]="table.at(i).controls.id" />

위의 구현에서는 개발자가 지루하고 오류가 발생하기 쉬운 사용자 지정 유형 캐스트를 수행해야 했습니다. IMHO, 이것은 강력한 형식의 Reactive Form을 사용할 이유를 완전히 잃습니다.

두 번째 접근 방식: ControlType으로 시작

단순 값 유형을 사용하면 원활하게 작동하지 않습니다. KeyValueControl을 사용하는 또 다른 아이디어가 떠오릅니다.T infer 를 사용하여 KeyValueControl에서 값 유형을 추출합니다.재귀적으로.

export type KeyValueControl<V> = {
  [key in keyof V]: V[key] & AbstractControl
}

export type InferTypedForm<Type> = Type extends TypedFormControl<infer X>
  ? X
  : Type extends Array<TypedFormControl<infer X1>>
  ? X1[]
  : Type extends Array<TypedFormGroup<infer X2>>
  ? Array<InferTypedFormGroup<X2>>
  : Type extends TypedFormGroup<infer X3>
  ? InferTypedFormGroup<X3>
  : Type extends TypedFormArray<infer X4>
  ? Array<InferTypedForm<X4>>
  : never

export type InferTypedFormGroup<T> = { [key in keyof T]: InferTypedForm<T[key]> }

export type InferTypedFormArray<T> = InferTypedForm<T[]>

export class TypedFormGroup<T extends KeyValueControl<T>> extends FormGroup {
  readonly controls: T
  readonly valueChanges: Observable<InferTypedFormGroup<T>>
  readonly statusChanges: Observable<FormStatus>
  readonly value: InferTypedFormGroup<T>
  ...
}

결과적으로,

interface Foo {
  first: TypedFormControl<string | null>
  last: TypedFormControl<string | null>
}

const table: FormArray<FormGroup<Foo>>

const row: FormGroup<Foo> = table.at(0) // typescript compile OK!

// type-safe binding in Angular HTML, all auto-complete (`id`, `username`) finally works!
<input [formControl]="table.at(0).controls.id" />

라이브 데모

Edit gaplo917/angular-typed-form-codesandbox

라이브 데모 IDE 스크린샷

그것은 복잡한 형태의 진정한 자동 완성 경험 입니다.
Screenshot 2020-06-12 at 19 02 10

각도 형식 형식

기존 반응형 모듈과 100% 호환
https://github.com/gaplo917/angular-typed-forms

의견 및 개선 사항을 환영합니다. 다가오는 Strongly Typed Reactive Forms가 Table 및 Nested Sub-Forms와 같은 복잡한 양식 모델을 처리할 수 있기를 바랍니다.

@IgorMinar , Angular 코어 팀Angular 커뮤니티 회원 .

이 긴 논평은 티켓 작성자가 " 관행에 반대하는 " 및 " 강력한 유형 "으로 강조한 두 가지 진술에 전적으로 초점을 맞추고 있습니다.

Strongly Typed Reactive Form에 대한 인터페이스 기반 접근 방식 대신에 클래스 기반 접근 방식을 선택해야 하지만 ng-stacks/forms 에 언급된 방식이 아닌 것을 제안

나의 제안

우리 모두는 OOP 관행에 익숙하기 때문에 이 클래스는 코드의 유연성과 유지 관리 가능성을 더 많이 제공합니다. 내가 아래에서 강조하는 몇 가지 이점:

  1. 코드를 분리합니다.
  2. 현재 접근 방식 및 인터페이스 기반 접근 방식에 비해 코드가 적습니다.
  3. 속성에서 사용자 지정 데코레이터를 사용할 수 있습니다.
  4. 코드는 읽기, 유지 관리 및 확장 가능합니다.
  5. 이 접근 방식을 사용하면 오류 메시지를 표시하기 위한 여러 조건과 함께 *ngIf 를 넣는 것처럼 템플릿에 비즈니스 로직을 작성할 필요가 없습니다. 템플릿은 비즈니스 로직을 작성하기 위한 것이 아니라고 생각합니다.
  6. 훨씬 많이...

위에서 언급한 인터페이스 코드를 Class 하고 Class 속성에 유효성 검사 데코레이터를 적용하겠습니다. 여기에서 우리는 단일 책임 원칙 관행을 따르고 있습니다. 아래 코드를 살펴보십시오.

image

몇 가지 경우를 고려하여 두 가지의 차이점을 이해하는 데 도움이 되는 인터페이스 및 클래스 기반 강력한 형식 접근 방식과 비교하겠습니다.

1. FormGroup 생성
여기에서 우리는 동일한 인스턴스를 사용하는 FormBuilder 와 같은 방법 group . 그러나 가져 오기 모듈 이름이 같은 다른 것 ReactiveTypedFormsModule 대신 ReactiveFormsModule . FormGroup 만들어 보겠습니다.
image

위의 코드에 따라 질문이 나옵니다.

ReactiveTypedFormsModule 가져온 후 현재 접근 방식이 작동합니까?
예, 작동합니다. ReactiveTypedFormsModule 가져온 후에는 아무 것도 변경되지 않습니다.

다른 사례들을 빠르게 살펴보고 이번 포스팅을 마치도록 하겠습니다.

2. FormControl의 값 변경
setValue 메서드를 호출하는 대신 Class 속성에 값을 직접 할당할 수 있습니다. 자동으로 FormControl 값을 설정합니다.

image

3. FormControl의 값 변경에 따른 비즈니스 로직 수행
FormControl ValueChanges 을 구독하는 대신 TypeScript에서 강력한 setter 메서드를 사용하십시오.

image

4. 입력 값 변환
우리는 Strongly-Typed에 초점을 맞추고 있지만 날짜와 같이 입력 컨트롤에서 오는 값은 String 형식으로 값을 얻고 있지만 TS 코드에서 Date 형식을 기대하고 있습니다. 이 문제를 극복하기 위해 필요에 따라 값을 변환하는 지시문이나 메서드를 만듭니다. 지시문을 생성하고 blah blah 작업을 수행해야 하기 때문에 약간 서투른 현재 코드를 여기에 표시하지 않겠습니다.

@toDate() // this will convert the value before assigning the value in 'dob' property.
dob:Date;

4. 중첩된 FormGroup FormControl의 값 변경
Nested FormGroup Object를 가져오는 대신 각 속성에 값을 직접 할당하고 SetValue 메서드를 호출할 수 있습니다.

image

5. 중첩된 FormArray에 FormGroup 추가하기
더 이상 할 말은 없습니다. 아래 코드를 살펴보세요 😄 .

image

HTML 템플릿에서 FormGroup 개체 참조

간단한 코드 😄 . HTML 템플릿에서는 아무 것도 변경되지 않지만 HTML 템플릿에서도 더 많은 것을 얻을 수 있습니다. 아래 코드를 참조하세요

<form [formGroup]="user.formGroup">
   <input type="text" formControlName="firstName"/>
   <small >{{user.formGroup.controls.firstName.errorMessage}}</small>
<!--Nested FormGroup-->
   <div [formGroup]="user.address.formGroup">...</div>
<!--Nested FormArray - Skills-->
   <div *ngFor="let skill of user.skills">
       <div [formGroup]="skill.formGroup">...</div>
   </div
</form>

Stackblitz Link : Strongly Type Reactive Form 작업 예제
Github의 예 : Strongly Typed Reactive Form

@ajayojha , 내가 틀렸다면 정정해 주지만 위의 귀하의 의견은 다음과 같이 축소될 수 있습니다.

  1. TypeScript 유형과 관련된 오버헤드는 좋지 않습니다.
  2. 데코레이터의 도움으로 런타임 유효성 검사 - 좋습니다.
  3. setter/getter가 있는 경우 setValue()valueChanges() 가 필요한 이유는 무엇입니까?

내 생각:

  1. TypeScript 유형을 작성하는 것은 정적 테스트를 작성하는 것과 같습니다. 누군가가 테스트 없이 응용 프로그램을 만들 수 있다고 생각하기 때문에 테스트가 필요하지 않다고 생각하지만 이는 나쁜 습관입니다.
  2. 데코레이터의 도움으로 런타임 유효성 검사 - 좋은 생각일 수 있습니다. 동의합니다.
  3. setValue() 외에도 patchValue() , reset() 있으며 이는 양식 값에서도 작동합니다. setValue() setter로 바꾸면 코드가 일관되지 않습니다. 또한 각 양식 모델 속성에 대해 setter를 작성해야 하는 경우 getter의 경우 훨씬 더 많은 코드 오버헤드와 성능 오버헤드가 추가됩니다. 양식 모델 속성 이름을 양식 제어 속성과 혼합하는 것도 제 생각에는 나쁜 생각입니다.

@KostyaTretyak 제 의견에 대한 귀하의 우려에 감사드립니다.

  1. TypeScript 유형과 관련된 오버헤드는 좋지 않습니다.

참고용으로 formgroup 개체는 강력한 형식이 지정됩니다. 인터페이스는 좋지만 모든 영역에 적합하지 않으며 TypeScript 유형이 나쁘다는 것이 아닙니다. 유형이 나쁘다고 언급한 곳은 없는 것 같습니다. 내 유일한 관심사는 인터페이스 접근 방식으로 소프트웨어 디자인 관행을 무시하고 있거나 잘못된 위치에서 인터페이스 접근 방식을 사용하고 있고 제안한 접근 방식은 클래스라고 말할 수 있기 때문에 인터페이스에 관한 것입니다. 클래스 접근 방식을 통해 이해하는 한 인터페이스에서 얻을 수 있는 TypeScript 유형의 이점을 손상시키지 않거나 가독성, 확장성 및 유지 관리 측면에서 인터페이스 접근 방식보다 더 많은 것을 얻을 수 있습니다.

Strongly Typed Reactive Form에 대한 올바른 인터페이스를 사용하고 있습니까?

인터페이스가 Strongly Typed Reactive Form에 대한 나쁜 습관(내 기준)이라는 측면에서 조금 더 설명하겠습니다.

TypeScript 유형은 훌륭하지만 인터페이스의 문제를 분명히 언급한 것처럼 소프트웨어 관행에 따르지 않는 모든 것을 뒤죽박죽으로 뒤섞어야 한다고 제안하지는 않습니다. 인터페이스에 대한 강조 표시된 관심사에 대해 생각하기만 하면 됩니다. 6k 이상의 구성 요소가 포함된 엔터프라이즈 응용 프로그램 중 하나에서 제 사례를 공유하겠습니다. 인터페이스 접근 방식을 사용하는 경우 개발 팀에서 변경하기 전에 좋은 질문을 할 것입니다.

  • 동일한 엔터티가 다른 속성을 가진 여러 구성 요소에서 사용되기 때문에 어디에 사용해야 하는 인터페이스입니다. 그렇다면 별도의 Interface Component를 현명하게 생성해야 합니까? 그렇다면 두 번째 경우를 읽으십시오.
  • 위의 접근 방식을 사용한다면 하나 이상의 구성 요소에서 두 인터페이스 속성을 모두 사용해야 하는 접근 방식은 무엇입니까? 이 문제를 해결하기 위해 인터페이스를 하나 더 만들고 확장할 수 있습니다. Strongly Type Reactive Form의 흔들림을 위해 점점 더 많은 파일을 생성하는 것이 좋은가요? 유지 관리 가능성은 어떻습니까? Angular 팀이 이 솔루션을 제공하므로 좋습니다. :) (Angular 팀이 Interface Approach를 선택할 경우) 답이 없습니다.
  • 우리가 + b 접근 방식을 사용한다면 일부 구성 요소에는 전부가 아닌 몇 가지 속성이 필요합니까? 개발 팀에 제공할 세 가지 솔루션이 있습니다.

    • 새 인터페이스를 만들고 새로 만든 인터페이스에 필요한 속성을 복사/붙여넣기합니다. 이것은 소프트웨어 세계에서 가장 더러운 접근 방식입니다. 이것은 단일 속성이 서버 측에서 변경될 때 많은 문제를 생성하고 속성 이름을 변경할 인터페이스의 수에서 영역을 유사하게 추적하기 어렵습니다.

    • nullable 속성을 설정합니다. nullable 속성을 정의하는 경우 'B' 접근 방식을 따라야 하는 이유는 무엇입니까? 다시 대답이 없습니다 :( 내 개발 팀에 제공합니다.

    • 다른 인터페이스를 만들지 말고 '부분적' 유틸리티 유형을 사용하고 모든 속성을 선택 사항으로 만드십시오. 이렇게 하면 인터페이스의 실제 이점을 잃게 됩니다. 이것은 또한 관행에 반대합니다. 내가 이것을 따라야한다면 왜 'A'접근법을 따라야합니까? 다시 대답이 없습니다. :).

    • 모든/몇 개의 속성을 nullable로 만드는 경우 코드 가독성은 어떻고 값을 서버에 전달하는 데 필요한 속성의 수를 어떻게 판단할 수 있습니까? 그런 다음 각 구성 요소를 확인하고 엿볼 수 있습니다. 주요 코드 가독성 문제.

이제 더 큰 관점에서 위의 경우를 생각하고 강력한 유형에 대한 반응형 형식의 인터페이스가 있는 TypeScript 유형과 비교하기만 하면 됩니다. 모든 좋은 접근 방식이 개발 시간을 절약할 수 있다고 믿으며 이 접근 방식에서는 소프트웨어 설계 원칙 및 관행에 따른 이점을 보지 못해서 죄송합니다.

  1. 데코레이터의 도움으로 런타임 유효성 검사 - 좋습니다.

Interface Approach에서 달성할 수 없는 데코레이터 접근 방식인 " 좋습니다 "에 대한 귀하의 의견에 동의합니다. 이것이 TypeScript의 가장 강력한 기능이라고 생각하는데, Reactive Form Approach에서 동일한 기능을 사용할 수 없고 개발 팀이 개체 속성을 완전히 제어할 수 있는 이유는 무엇입니까?

  1. setter가 있는 경우 setValue()가 필요한 이유는 무엇입니까?

어디에서 'setValue()'가 필요하다고 말했습니까? 나는 setValue가 필요하지 않으며 Class Driven Approach에서 setValue 메서드를 호출하는 예제를 보여주지 않았습니다. 내가 틀렸다면 저를 수정하십시오.

  1. TypeScript 유형을 작성하는 것은 정적 테스트를 작성하는 것과 같습니다. 누군가가 테스트 없이 응용 프로그램을 만들 수 있다고 생각하기 때문에 테스트가 필요하지 않다고 생각하지만 이는 나쁜 습관입니다.

TypeScript 유형이 정적 테스트를 작성하는 것과 같다고 말하는 것이 아닙니다. 그러나 반응형 기본 클래스의 커밋 변경에 동의하지 않습니다. 클래스 정의를 건드리지 않고도 동일한 결과를 얻을 수 있다고 생각합니다. 여기에서 지금까지 커밋에 따라 사용하지 않은 인터페이스의 실제 기능을 사용할 수 있습니다. 로직이 너무 오래 실행되고 기본값 ' 어느'?
Reactive Form의 기본 클래스를 건드리지 않고도 동일한 결과를 얻을 수 있다고 생각합니다. 기본 클래스 정의를 변경하고 사양도 변경하는 대신 인터페이스를 활용하지 않는 이유를 모르겠습니다.

  1. 데코레이터의 도움으로 런타임 유효성 검사 - 좋은 생각일 수 있습니다. 동의합니다.

우리 둘 다 이 페이지에서 같은 페이지라는 것을 아는 것이 좋습니다. :).

  1. setValue() 외에도 양식 값과 함께 작동하는 patchValue(), reset()도 있습니다. setValue()만 setter로 바꾸면 코드가 일관되지 않습니다. 또한 각 양식 모델 속성에 대해 setter를 작성해야 하는 경우 성능 오버헤드와 함께 훨씬 더 많은 코드 오버헤드가 추가됩니다. 양식 모델 속성 이름을 양식 제어 속성과 혼합하는 것도 제 생각에는 나쁜 생각입니다.

위의 점을 호출 방법, setter 성능 오버헤드, 혼합 형태 모델 속성의 세 부분으로 나누어 설명하겠습니다.

호출 방법: 예상대로 이 게시물을 작성하는 동안 누군가가 'patchValue' 또는 'reset' 방법을 사용하도록 제안할 수 있다고 생각했습니다. 다시 말하지만 실제 사례에서는 대부분 개발 팀이 patchValue 또는 다른 방법 대신 'setValue' 방법을 사용하고 있습니다(Angular Application Code Review 및 Stackoverflow Posts setValue 대 patchValue에 따른 제 경험입니다). 내 초점은 우리가 어떤 방법을 호출하든 상관없이 값을 할당하기 위한 메서드를 호출하는 것입니다.

세터 성능 : 세터가 성능 오버헤드를 생성한다는 진술에 동의합니다. 이 경우 반응 형식을 바인딩하기 위해 Angular Framework가 Control Value Accessor 클래스 및 기타 많은 지시문에서 setter 메서드를 사용하고 있기 때문에 Angular Project에서 먼저 수정해야 합니다. 클래스 접근 방식. @Input 데코레이터를 사용하여 여러 구성 요소에서 사용하는 것과 동일한 접근 방식을 한 가지 더 찾거나 Angular 팀에서 이러한 종류의 성능 문제를 극복하기 위해 다른 솔루션을 제공해야 합니다(내 생각에는). 그래서 저는 이것이 큰 걱정거리가 아니라고 생각합니다. 이제 성능 문제에 관해서는 기존 접근 방식 및 setter 방식 접근 방식과 비교하십시오(선택 사항이며 개발 팀이 Angular의 ChangeDetectionStrategy와 동일하게 원하는 경우 선택할 수 있습니다. 선택에 대해서는 rxweb 문서 사이트의 예제를 참조하십시오. 이 경우 값을 구독할 때 얼마나 많은 함수가 호출되는지 판단하고 값을 설정하거나 직접 setter 메서드를 호출한 후 변경합니다. 나는 이것이 값 변경, 낮은 빌드 크기에 비해 코드 실행이 적은 측면에서 훨씬 더 직관적이라고 생각합니다. 패키지, 코드 가독성 및 기타 많은 장점이 있습니다.

속성 혼합 : 귀하의 의견은 무엇입니까? 서버에서 반환한 속성 이름과 다른 FormControl 속성 이름을 할당하고 있습니까? 예인 경우 서버에 게시하기 전에 속성 이름을 변경해야 할 때마다 죄송하지만 애플리케이션 전체에서 선호하지 않기 때문에 이것이 코드의 주요 문제라고 말하고 싶습니다. 평균 40개 이상의 필드가 포함된 내 애플리케이션 양식에 대한 귀하의 좋은 의견을 고려하면 모든 속성 값을 수동으로 설정해야 합니다. 값과 제품 빌드 크기를 할당하는 흔들림에 대한 구성 요소의 코드에 대해 생각하기만 하면 됩니다. 이것이 클래스 접근 방식보다 더 나은 의견입니까?
이제 제안된 솔루션에 도달했습니다. 우리는 두 가지를 하나로 혼합하지 않습니다. FormControl 속성이 다르고 클래스 속성이 각 데이터 유형과 다릅니다. FormControl 속성 이름이 Data 속성과 다른 것처럼 속성 이름을 변경하려면 rxweb 반응형 양식 패키지 설명서를 참조하세요. 따라서 나쁜 느낌(속성 이름과 양식 컨트롤 이름 혼합)이 제안된 접근 방식에 솔루션이 있으므로 문제가 없습니다.

귀하의 모든 우려 사항에 대한 답변이 되었기를 바랍니다. 이에 대한 다른 우려 사항이 있으면 언제든지 공유해 주십시오. :)

이전 의견에서 언급했듯이 Interface Segregation Principle Practices의 힘을 사용하여 동일한 것을 달성할 수 있기 때문에 Reactive Form의 기본 클래스를 변경할 필요가 없습니다. 다음은 @rxweb/types 패키지가 포함된 End-to-End Strongly-Typed Reactive Form 솔루션입니다. 이것은 인터페이스뿐만 아니라 클래스 접근 방식에서도 잘 작동합니다. :)

구현 후 코드는 어떻게 생겼습니까?

스택블리츠: 열기
Github : 강력한 형식의 인터페이스 기반 반응형 예제

누군가 제안이 있으면 자유롭게 공유할 수 있습니다.

따라서 Angular의 버전 10을 사용할 수 있습니다 . 이것은 주요 릴리스이며 분명히 반응하는 형식은 최소한 Angular의 버전 11까지는 강력한 형식이 지정되지 않습니다. 따라서 이 기능을 구현하려면 적어도 가을까지는 기다려야 합니다.

여기에서 본 대부분의 제안/PR에서 양식 모델이 구축되는 방식에 대해 질문(또는 제안?)이 있습니다.

Reactive Forms 유형을 안전하게 만들려는 대부분의 라이브러리와 PR을 보면 다음과 같은 모델을 생성한다는 것을 알 수 있습니다.

interface Address {
  name: Name;  
}
interface Name {
  firstName: string;
  lastName: string;
}

이것은 다음과 같이 "번역"됩니다.

const myForm = new FormGroup<Address>({
  name: new FormGroup<Name>({
    firstName: new FormControl('John'),
    lastName: new FormControl('Doe'),
  })
})

따라서 간단히 말해서 "객체라면 이에 대한 FormGroup을 작성하고 배열이면 FormArray를 작성하고 기본 값이면 FormControl을 작성하십시오."

그러나 한 가지 문제가 있습니다. 더 이상 FormControls에서 개체를 사용할 수 없습니다.

내가 지금까지 본 솔루션: 일부 라이브러리는 단순히 이것을 지원하지 않습니다. 그리고 일부 라이브러리는 일종의 "해킹"을 사용하여 실제로 FormGroup 대신 FormControl을 사용하려는 힌트를 만듭니다.

내 질문/제안: 다음과 같이 양식 모델을 명시적으로 정의하는 것에 반대하는 것은 무엇입니까?

interface Address {
  name: FormGroup<Name>;  
}
interface Name {
  firstName: FormControl<string>;
  lastName: FormControl<string>;
}

const myForm = new FormGroup<Address>({
  name: new FormGroup<Name>({
    firstName: new FormControl('John'),
    lastName: new FormControl('Doe'),
  })
})

이것은 이제 개체를 FormControls에 넣을 수 있다는 큰 이점이 있습니다. 그리고 그렇게하기 위해 어떤 종류의 "해킹"도 필요하지 않습니다. :)

이를 위해 Codesandbox를 만들었습니다. 직접 사용해 볼 수 있습니다. https://codesandbox.io/s/falling-grass-k4u50?file=/src/app/app.component.ts

@MBuchalik , 이것은 "강력한 형식의 형식" 작업을 시작할 때 마음에 떠오르는 첫 번째 명백한 결정입니다. 나도 이것으로 시작했지만, 두 가지 모델을 만들어야 한다는 심각한 단점이 있습니다. 하나는 양식 컨트롤용이고 다른 하나는 양식 값용입니다.

반면에 내가 이해하는 한 이 솔루션을 사용하면 중단 없이 "강력한 형식"을 구현할 수 있으며 Angular의 다음 주요 버전이 출시될 때까지 기다릴 필요가 없습니다. 여기에서 이러한 솔루션으로 실제로 작업하여 두 모델을 생성해야 하는 것보다 더 중요한 단점이 있는지 여부를 평가할 필요가 있습니다.

@MBuchalik 나는 당신과 같은 의견을 공유했고 PR에 같은 질문을 제기했고 각도 기고자 중 한 명( @KostyaTretyak )이 대답했습니다.

PR에서 토론을 살펴볼 수 있습니다.
https://github.com/angular/angular/pull/37389#discussion_r438543624

TLDR;

다음은 몇 가지 문제입니다.
귀하의 접근 방식을 따르면 양식 컨트롤과 양식 값의 두 가지 다른 모델을 만들어야 합니다.
양식 컨트롤의 모델은 읽기가 더 어렵습니다.

그리고 반년 전에 이미 프로덕션에 사용된 프로젝트에 이 아이디어를 구현했습니다 . Angular HTML의 컨트롤 유형에 대한 전체 정적 자동 완성 환경은 주니어 개발자 생산성을 실제로 높여줍니다. ( fullTemplateTypeCheck 활성화됨)

이 스레드의 이전 댓글에서 "내가 이 길을 가는 이유"를 공유했습니다.
https://github.com/angular/angular/issues/13721#issuecomment -643214540

Codesanbox 데모: https://codesandbox.io/s/github/gaplo917/angular-typed-form-codesandbox/tree/master/?fontsize=14&hidenavigation=1&theme=dark

@KostyaTretyak 및 @gaplo917에 대한 통찰력에 감사드립니다! 👍

제가 제대로 이해했다면 다음과 같이 요약할 수 있습니다.

하나의 단일 모델만 사용하려는 경우 @KostyaTretyak에서 제공하는 것과 같은 솔루션을 사용할 수 있습니다. 그러나 단점은 이제 더 이상 FormControls에서 개체를 사용할 수 없다는 것입니다. (나는 이것을 허용하는 "해킹"이 있다는 것을 알고 있습니다. 그러나 우리 모델은 다시 "깨끗한" 것이 아니므로 다시 한 번 2개의 모델이 필요합니다.)

FormControls에서 개체를 사용할 수 있도록 하려면 I(또는 @gaplo917)에서 설명한 것과 같은 접근 방식을 사용하는 방법이 없을 것입니다(!). 단점은 기본적으로 2개의 모델이 필요하다는 것입니다. 또는 최소한 일부 도우미 유형을 사용하여 양식 값 모델을 "추출"하십시오.

따라서 이제 FormControl의 개체가 가능한지 여부에 대해 생각하면 됩니다. 이렇게 하면 두 가지 접근 방식 중 어느 것을 선택할 것인지에 대한 질문에 간단히 답할 수 있습니다. 아니면 내가 뭔가를 놓치고 있습니까?

@KostyaTretyak 및 @gaplo917에 대한 통찰력에 감사드립니다! 👍

제가 제대로 이해했다면 다음과 같이 요약할 수 있습니다.

하나의 단일 모델만 사용하려는 경우 @KostyaTretyak에서 제공하는 것과 같은 솔루션을 사용할 수 있습니다. 그러나 단점은 이제 더 이상 FormControls에서 개체를 사용할 수 없다는 것입니다. (나는 이것을 허용하는 "해킹"이 있다는 것을 알고 있습니다. 그러나 우리 모델은 다시 "깨끗한" 것이 아니므로 다시 한 번 2개의 모델이 필요합니다.)

FormControls에서 개체를 사용할 수 있도록 하려면 I(또는 @gaplo917)에서 설명한 것과 같은 접근 방식을 사용하는 방법이 없을 것입니다(!). 단점은 기본적으로 2개의 모델이 필요하다는 것입니다. 또는 최소한 일부 도우미 유형을 사용하여 양식 값 모델을 "추출"하십시오.

따라서 이제 FormControl의 개체가 가능한지 여부에 대해 생각하면 됩니다. 이렇게 하면 두 가지 접근 방식 중 어느 것을 선택할 것인지에 대한 질문에 간단히 답할 수 있습니다. 아니면 내가 뭔가를 놓치고 있습니까?

@MBuchalik 제 생각에는 Typescript 컴파일러를 신뢰하고 "유형 추론" 기능에 크게 의존한다면 2개의 모델이 필요하지 않습니다. 내부 시스템에는 60개 이상의 형식이 있으며 그 중 일부는 3개의 깊이 수준 FormArray-FormGroup-FormArray 중첩되어 매우 복잡하며 값 유형에 대한 명시적 모델도 필요하지 않습니다.

다음과 같은 두 가지 유형의 데이터 모델만 사용할 수 있습니다.

  • API 데이터 요청/응답 모델
  • FormControl 모델

시간의 99.9%, 우리는

  1. 각 복잡한 양식에 대한 캡슐화 만들기
  2. 원격 데이터 변환 -> 양식 데이터
  3. 양식 데이터 변환 -> 원격 페이로드

다음 코드 스니펫은 그림입니다.

interface FooApiData {
   id: string
   age: number
   dob: string | null
   createdAt: string
}

interface FooFormControlType {
  id: TypedFormControl<string>
  age: TypedFormControl<number>

  // calendar view required JS date form control binding
  dob: TypedFormControl<Date | null>
} 

interface FooApiUpdateRequest {
   id: string
   dob: string | null
   age: number
}

class FooForm extends TypedFormGroup<FooFormControlType> {
    constructor(private fb: TypedFormBuilder, private initialValue: FooApiData) {
      super({
          id: fb.control(initialValue.id, Validators.required),
          dob: fb.control(initialValue.dob === null ? new Date(initialValue.dob) : null),
          age: fb.number(initialValue.age, Validators.required)
       })
   }
   toRequestBody(): FooApiUpdateRequest {
       const typedValue = this.value
       return {
          id: typedValue.id,
          dob: typedValue.dob !== null ? moment(typedValue.dob).format('YYYYMMDD') : null,
          age: typedValue.age
       }
   }
}

const apiData = apiService.getFoo()

const form = new FooForm(new TypedFormBuilder(), apiData)

// assume some UI changes the form value
function submit() {
   if(form.dirty && form.valid){
     const payload = form.toRequestBody()
     apiService.updateFoo(payload)
   }
}

PS Typescript에서 type-safe하게 프로그래밍을 즐길 수 있는 독창적인 데이터 흐름 아키텍처입니다.

하나의 단일 모델만 사용하려는 경우 @KostyaTretyak에서 제공하는 것과 같은 솔루션을 사용할 수 있습니다. 그러나 단점은 이제 더 이상 FormControls에서 개체를 사용할 수 없다는 것입니다. (나는 이것을 허용하는 "해킹"이 있다는 것을 알고 있습니다. 그러나 우리 모델은 다시 "깨끗한" 것이 아니므로 다시 한 번 2개의 모델이 필요합니다.)

여기서 FormControl 대한 개체를 얼마나 자주 사용해야 하는지 추정해야 합니다. 대략 5~30% 정도 예상할 수 있을 것 같습니다. 즉, 하나의 모델로 솔루션을 사용하면 FormControl 를 사용하는 경우의 70-95%를 커버할 수 있습니다. 나머지는 - TypeScript에 대한 힌트를 추가 유형으로 제공하십시오( Control<T> , "두 번째 모델"이라고 부르는 것은 옳지 않습니다):

interface FormModel {
  date: Control<Date>;
}

Control<T> 유형을 해킹이라고 할 수 있습니까? - 예, 아마도 해킹일 수 있지만 대략적인 해킹은 아닙니다. 이 유형이 의도한 대로 작동하지 않거나 부작용이 있는 경우를 알지 못합니다.

아, 폼 값 모델에 외부 라이브러리를 사용해야 할 때 Control<T> 부작용을 기억했습니다. 이러한 경우 두 가지 모델이 실제로 필요합니다.

import { FormBuilder, Control } from '@ng-stack/forms';

// External Form Model
interface ExternalPerson {
  id: number;
  name: string;
  birthDate: Date;
}

const formConfig: ExternalPerson = {
  id: 123,
  name: 'John Smith',
  birthDate: new Date(1977, 6, 30),
};

interface Person extends ExternalPerson {
  birthDate: Control<Date>;
}


const fb = new FormBuilder();
const form = fb.group<Person>(formConfig); // `Control<Date>` type is compatible with `Date` type.

const birthDate: Date = form.value.birthDate; // `Control<Date>` type is compatible with `Date` type.

그러나 이 코드에서 오버헤드는 여기에만 있습니다.

interface Person extends ExternalPerson {
  birthDate: Control<Date>;
}

@ArielGueta 덕분에 Control<T> 유형의 중요한 문제 가 알려졌습니다. 즉, 이전에 계획한 대로 향후 Angular에 대한 Pull Requests에서 Control<T> 를 구현하려고 하지도 않습니다.

@ArielGueta 덕분에 Control<T> 유형의 중요한 문제 가 알려졌습니다. 즉, 이전에 계획한 대로 향후 Angular에 대한 Pull Requests에서 Control<T> 를 구현하려고 하지도 않습니다.

@KostyaTretyak 사실이 아닙니다. 중요한 문제 는 "ControlType" 구현이 올바르지 않다는 것만 보여줍니다.

완전한 "Control Type" 구현은 문제가 없습니다.

라이브 데모: https://codesandbox.io/s/lucid-bassi-ceo6t?file=/src/app/demo/forms/type -test.ts

Screenshot 2020-07-01 at 00 35 11

즉, Control을 구현하려고 하지도 않습니다.이전에 계획한 대로 Angular에 대한 향후 Pull Requests에서.

좋아요, 즉, PR(및 연속적인 것)이 FormControls의 개체를 지원하지 않을 가능성이 가장 높습니까?

@MBuchalik , 현재(Angular v10), 다음과 같은 형식 모델이 있는 경우:

interface FormModel {
  date: Date;
}

구성 요소에서 date 속성 값에 액세스하려면 다음을 수행해야 합니다.

get date() {
  return this.formGroup.get('date') as FormControl;
}
// ...
this.date.value as Date;

내 현재 pull 요청 은 양식 값에 대한 제네릭을 제공하지만 양식 제어를 위한 유형은 제공하지 않습니다.

get date() {
  return this.formGroup.get('date') as FormControl<Date>;
}
// ...
this.date.value; // Here Date type

@gaplo917 , @MBuchalik , 나는 당신의 솔루션을 시도했고 내 자신의 유사한 솔루션을 구현하려고 시도했지만 모두 완벽하게 작동하지 않습니다. 이 솔루션은 또한 양식 모델 값을 재귀적으로 추출하기 위한 유형 세트를 제공합니다. 오버헤드 및 주요 변경 사항은 매우 중요합니다( PR 초안 참조).

현재로서는 이러한 솔루션이 Angular에서 구현되도록 제안되어야 하는지 의심스럽습니다. 즉, 지금은 양식 컨트롤 유형이 아닌 양식 값에만 제네릭을 사용해야 합니다.

그러나 그들은 모두 완벽하게 작동하지 않습니다

저는 제 일러스트레이션에 몇 시간만 투자했기 때문에 완벽할 것이라고 기대하지 않았습니다 ;) 잘 작동하지 않는 것들에 대한 예를 들어 주시겠습니까? (특히 당신의 관점에서 보면 쉽게 고칠 수 없는 일이라면?)

Btw, 이전 버전과의 호환성에 관한 한 가지 제안: 내 관점에서 볼 때 구현을 완전히 이전 버전과 호환되게 하는 것은 상대적으로 어렵습니다. 이 때문에 다음을 수행할 수 있습니다. FormControl, FormGroup 및 FormArray 클래스를 전혀 변경하지 않습니다. 대신, 우리는 그것들로부터 상속되는 새로운 것을 생성합니다( StrictFormControl<T>StrictFormGroup<T> 또는 원하는 무엇이든 호출할 수 있음). 이것들은 우리가 유형을 안전하게 만드는 것들입니다. 이점: 우리는 주요 변경 사항이 수행되지 않았다고 100% 확신합니다. :)

내 일러스트레이션에 몇 시간, 그래서 나는 그것이 완벽할 것이라고 기대하지 않았습니다 ;)

나는 이 솔루션으로 며칠 동안 작업했고 양식 작업이 얼마나 어려울지 알았습니다.

  1. 우선 상당한 오버헤드와 두 가지 모델이 필요합니다.
  2. 둘째, 이 솔루션은 Control<T> 유형의 솔루션보다 안정성 면에서 더 좋지 않습니다. 동일한 방식으로 양식 모델의 값을 재귀적으로 추출해야 하기 때문입니다.
  3. 중첩된 양식 컨트롤로 작업합니다. 다음과 같은 양식 모델이 있는 경우:
interface FormModel {
  one: FormGroup<{two: FormControl<string>}>;
}

그리고 formGroup.controls.one.value 를 받으면 TypeScript는 {two: string} 유형이 아닌 조건부 유형의 힌트를 제공합니다(당연히 있어야 함). 따라서 IDE에서 읽기 어려운 값입니다.

그리고 formGroup.controls.one.value를 얻으면 TypeScript는 {two: string} 유형이 아닌 조건부 유형의 힌트를 제공합니다(당연히 있어야 함). 따라서 IDE에서 읽기 어려운 값입니다.

알겠습니다. 모든 것을 올바르게 이해했는지 확인하십시오. 내 구현을 사용하고 다음을 작성하는 경우:

interface FormModel {
  one: FormGroup<Two>;
}
interface Two {
  two: FormControl<string>;
}

const myForm = new FormGroup<FormModel>({
  one: new FormGroup<Two>({
    two: new FormControl('')
  })
});

(좀 더 장황하게 만들었습니다 ;) )

이제 myForm.controls.one.value 를 찾으면 다음과 같습니다.

grafik

이 예에서 "2"는 선택 사항이 아니어야 한다고 말씀하십니까? 이것은 양식 값을 입력하는 올바른 방법이 아닌 것 같습니다. 양식 값에는 비활성화되지 않은 필드만 포함됩니다. 따라서 내 관점에서 재귀적 Partial이어야 합니다. 컴파일 타임에 어떤 필드가 비활성화되고 어떤 필드가 비활성화되는지 알 수 없습니다.

이 예에서 "2"는 선택 사항이 아니어야 한다고 말씀하십니까?

뭐? 아니.

귀하의 솔루션에 대한 내 테스트:

interface FormModel {
  one: FormGroup<{two: FormControl<string>}>;
}

let formGroup: FormGroup<FormModel>;
const some = formGroup.controls.one.value;

value 에 마우스를 올려놓은

(property) FormGroup<FormGroupControls<{ two: FormControl<string>; }>>.value: PartialFormGroupValue<FormGroupControls<{
    two: FormControl<string>;
}>>

여기서 PartialFormGroupValue 는 조건부 유형 PartialFormValue 냅니다.

아, 알겠습니다. 그럼 글자가 읽기 어렵다는 말씀이시죠? 나는 원래 당신이 버그나 그런 것에 대해 이야기하고 있다고 생각했습니다.

글쎄, 대부분의 IDE는 계속 입력하면 사용 가능한 속성에 대한 제안을 계속 표시합니다. 그래서 저는 여기에 큰 문제가 없다고 봅니다. (물론 그냥 {two?: string} 라고 써져 있으면 읽는게 더 나을텐데.. 그렇게까지 중요한건 아니라고 생각합니다. 최소한 제 생각입니다.)

Control<T> 를 구현했다면 나처럼 sth를 수행하지 않고 양식 값을 입력할 때 어떻게 제거할까요? 그리고 도우미 유형을 사용하지 않고 양식 값을 재귀 부분적으로 만들려면 어떻게 해야 합니까?

컨트롤을 구현한 경우, 그런 다음 나처럼 sth를 수행하지 않고 양식 값 입력에서 어떻게 제거합니까? 그리고 도우미 유형을 사용하지 않고 양식 값을 재귀 부분적으로 만들려면 어떻게 해야 합니까?

이 경우 내 솔루션이 더 좋지 않습니다.

(property) FormGroup<FormGroup<{ two: FormControl<string>; }>>.value: ExtractGroupValue<FormGroup<{
    two: FormControl<string>;
}>>

당신이 그것을 요청했기 때문에 나는이 예를 주었다 :

잘 작동하지 않는 것들에 대한 예를 들어 주시겠습니까? (특히 당신의 관점에서 보면 쉽게 고칠 수 없는 일이라면?)

그건 그렇고, 나는 Control<T> 중요한 문제 를 수정했습니다.

Angular 10 및 [formControl] 의 HTML 바인딩 문제를 해결하기 위해 다음과 같이 했습니다.

다른 문제(https://github.com/angular/angular/issues/36405#issuecomment-655110082)에서 언급했듯이 내 양식의 경우 일반적으로 재사용 및 테스트를 용이하게 하기 위해 FormGroup 를 확장하는 클래스를 만듭니다. . 이 구조를 사용하여 다음과 같이 코드를 업데이트하여 현재 문제를 해결할 수 있었습니다.

class UserFormGroup extends FormGroup {
  constructor() {
    super({
      id: new FormControl(null, Validators.required),
      name: new FormControl(null, Validators.required),
    });
}

이에:

// everything will extend these two
export class EnhancedFormGroup<T extends { [key: string]: AbstractControl }> extends FormGroup
{
  controls!: T;
}
export class EnhancedFormArray<T extends AbstractControl> extends FormArray 
{
  controls!: T[];
}

// reworked form from above
function formDefinition() {
   return {
      id: new FormControl(null, Validators.required),
      name: new FormControl(null, Validators.required),
    };
}

class UserFormGroup extends EnhancedFormGroup<ReturnType<typeof formDefinition>> {
  constructor() {
    super(formDefinition());
}

이 시점에서 form.controls 는 해당 유형을 { id: FormControl, name: FormControl } 로 올바르게 표시하고 HTML에서 올바르게 바인딩하며 양식이 중첩된 양식 그룹 또는 배열로 더 복잡한 경우 올바르게 집계됩니다.

formDefinition 함수를 사용하는 것은 아름답지 않지만 양식 정의와 생성자 간의 중복을 방지하기 위해 생각해낼 수 있는 가장 깨끗한 솔루션이었습니다.

나는 업데이트 수 있으리라 생각 FormGroup 동적으로 추가하는 형태 / I 추측 제거 컨트롤에 대한 사실이 아닐 수도 있지만, 잘 (깨는 변화를 도입하지 않고 제네릭 형식 정의 위를 가지고, 그들이에 표시되지 것입니다 controls 유형)

편집하다
FormGroup을 확장하는 클래스를 만들 필요가 없다면 훨씬 더 간단한 것처럼 보입니다. 일반적인 문제를 해결하는 도우미 함수를 만들 수 있습니다.

function createEnhancedFormGroup<T extends { [key: string]: AbstractControl }>(controls: T) {
  return new EnhancedFormGroup<T>(controls);
}

const form = createEnhancedFormGroup({
  id: new FormControl(null, Validators.required),
  name: new FormControl(null, Validators.required),
});

편집 2
... 또는 FormGroup 클래스 자체에 베이킹할 수 있습니다( FormBuilder 아마도?)

export class EnhancedFormGroup<T extends { [key: string]: AbstractControl }> extends FormGroup
{
  controls!: T;

  static create<T extends { [key: string]: AbstractControl }>(controls: T) {
    return new EnhancedFormGroup<T>(controls);
  }
}

const form = EnhancedFormGroup.create({
  id: new FormControl(null, Validators.required),
  name: new FormControl(null, Validators.required),
});

편집 3
value 에 대한 입력을 포함하도록 위의 예를 확장하고 모든 것을 요약하는 기사를 만들었습니다.

https://medium.com/youngers-consulting/angular-typed-reactive-forms-22842eb8a181

이것은 이제 미래 개발을 위한 로드맵에 표시됩니다. https://angular.io/guide/roadmap#better -developer-ergonomics-with-strict-typing-for-angularforms

@pauldraper ~2개월 전 로드맵과 비교하여 변경된 점을 설명해 주시겠습니까? 내가 볼 수있는 유일한 변경 사항은 제목의 문구입니다. 그러나 그것은 여전히 ​​"미래"섹션에 있습니다. 마치 2개월 전의 일처럼.

@MBuchalik 아마도 2개월 동안 있었던 것 같습니다.

이 페이지가 도움이 되었나요?
0 / 5 - 0 등급