Angular 强类型反应形式

Angular strongly typed reactive forms

我希望重构我的 Angular 项目中的大量组件,以具有强类型的 FormGroups、FormArrays 和 FormControls。

我只是在寻找一种实现强类型响应式表单的好方法。谁能根据自己的经验提供suggestions/recommendations?

谢谢。

编辑:

澄清一下,我所说的强类型是指目前当我创建 FormGroup 或 FormArray 时,我无法指定其中实际表单的结构。当我将此表单传递到我的应用程序中的各个组件时,我觉得我让维护变得更加困难。

最优雅的解决方案是利用 TypeScript 声明文件 (*.d.ts) 引入扩展标准形式 类 的通用接口,如 AbstractControlFormControl 等。它不引入任何新功能并且在编译后没有占用空间 JavaScript,但同时执行强类型检查。

现在 suggested by Daniele Morosinotto in March this year and there are talks 将其包含在 Angular 9.

采用解决方案很简单:

  1. this gist下载TypedForms.d.ts并在你的项目中保存为src/typings.d.ts(Angular 6+已​​经知道如何使用这个文件)。
  2. 只要您需要强类型验证(请参阅 that gist or stackblitz 中的示例),就开始使用新类型(FormGroupTyped<T>FormControlTyped<T> 等)。

有关详细信息,请查看 blog post 分析强类型表单的可用解决方案。

我最终使用的解决方案是 library I found called ngx-strongly-typed-forms

它使您能够拥有强类型的 FormControls、FormGroups 和 FormArrays。有一些限制,但它确实对我的项目有很大帮助。

您可以在 https://github.com/no0x9d/ngx-strongly-typed-forms

查看文档

我遇到了类似的问题,这是我的解决方案。我真的只关心表单的 'value' 类型,而不关心表单本身。它最终看起来像这样。

export interface UserFormValue {
  first_name: string
  last_name: string
  referral: string
  email: string
  password: string
}
...

ngOnInit() {
  this.userForm = this.fb.group({
    first_name: [ '', Validators.required ],
    last_name: [ '', Validators.required ],
    referral: [ '' ],
    email: [ '', [ Validators.required, Validators.email ] ],
    password: [ '', [ Validators.required, Validators.minLength(8) ] ],
  });
}

...

然后在模板中提交值

<form [formGroup]="userForm" (ngSubmit)="onSubmit(userForm.value)">
   ...
</form>

现在您可以向提交功能添加类型

onSubmit(userForm: UserFormValue) {
   ...
}

它并不完美,但对于我的用例来说已经足够好了。我真希望有这样的。

userForm: FormGroup<UserFormValue>

对于那些想要其他解决方案的人。我发现 this article 正在谈论 angular 形式的强类型。以下是我的总结。

interface Person {
  name: string;
  email: string
}

// Controls in a form group that would emit a Person as it's value
type PersonControls = { [key in keyof Person]: AbstractControl };
type PersonFormGroup = FormGroup & { value: Person, controls: PersonControls };

export class MyFormComponent {
  form = new FormGroup({
    name: new FormControl(),
    email: new FormControl()
  } as PersonControls) as PersonFormGroup;

  init() {
    const name = this.form.controls.name; // strong typed!
  }
}

如果您有嵌套类型的组,那么您可以这样做:
**models.ts

export type TCreateUserFields = {
    first_name: string,
    last_name: string,
    accept_terms: boolean,
};
export type TPasswordsControls = {
    passwords: FormGroup & {
        password: AbstractControl,
        confirm_password: AbstractControl
    }
}
export type TPasswordsFields = {
    passwords: {
        password: string,
        confirm_password: string
    }
}
export type TAllFields = TCreateUserFields & TPasswordsFields;
export type TAllControls = TCreateUserControls & TPasswordsControls;
export type TCreateUserControls = {
    [key in keyof TCreateUserFields]: AbstractControl
};
export type TCreateUserFormGroup = FormGroup & {value: TAllFields, controls: TAllControls};

**component.ts
this.registerationForm = this.fb.group(
{
    first_name: new FormControl("", [Validators.required]),
    last_name: new FormControl("", [Validators.required]),
    accept_terms: new FormControl(false, [Validators.required]),
    passwords: new FormGroup(
        {
            password: new FormControl("", [Validators.required, Validators.pattern(/^[~`!@#$%^&*()_+=[\]\{}|;':",.\/<>?a-zA-Z0-9-]+$/)]),
            confirm_password: new FormControl("", [Validators.required, Validators.pattern(/^[~`!@#$%^&*()_+=[\]\{}|;':",.\/<>?a-zA-Z0-9-]+$/)]),
        }, { 
            validators: <ValidatorFn>pwdConfirming({key:'password', confirmationKey:'confirm_password'})
        }
    )
} as TCreateUserControls) as TCreateUserFormGroup;

自 Angular 14 日(目前在 next 频道)开始提供严格类型的表格!

FormGroupFormArray classes 接受泛型,这是内部控件的类型。 FormControl class 接受其值类型的泛型。还有一个名为 FormRecord 的新 class,用于动态控件组。

这是一个例子:

const party = new FormGroup({
  address: new FormGroup({
    house: new FormControl(123, {initialValueIsDefault: true}),
    street: new FormControl('Powell St', {initialValueIsDefault: true}),
  }),
  formal: new FormControl(true),
  foodOptions: new FormArray([
    new FormControl('Soup'),
  ])
});

// whichHouse has type `number`
const whichHouse = party.get('address.house')!.value;

// Error: control "music" does not exist
const band = party.controls.music;