我的 Angular 偏好:使用 formControl 指令代替 formControlName

112 阅读2分钟

正如标题所说,为了类型安全和简洁性,我最终选择使用formControl 指令来代替 formControlName

类型安全

给定这个formGroup:

formGroup = this.formBuilder.group({
  booker: "",
  bookNames: this.formBuilder.array([new FormControl("", Validators.required)]),
});

使用formControlName的HTML可能是这样的:

<div [formGroup]="formGroup">
  <input
    [placeholder]="'please enter the booker name'"
    formControlName="booker"
  />
  <ng-container formArrayName="bookNames">
    <input [placeholder]="'please enter the book name'" formControlName="0" />
  </ng-container>
</div>

到目前为止,如果字段名bookerbookNames0中有拼写错误,在编辑器中您不会看到任何错误。只有在运行时才会看到错误,例如ERROR Error: Cannot find control with name: 'booker1'

然而,当不正确地绑定formControl时,由于它是TypeScript,您会立即在编辑器中看到类似Property 'booker1' does not exist on type '的错误。

<div>
  <input
    [placeholder]="'please enter the booker name'"
    [formControl]="formGroup.controls.booker"
  />
  <input
    [placeholder]="'please enter the book name'"
    [formControl]="formGroup.controls.bookNames.at(0)"
  />
</div>

类型检查对您有很大帮助,尤其是在重命名字段名时。

简洁性

基于上面的例子,您可以看到我们不需要formGroupformArrayName来与formControl一起工作。但是当我们使用formControlName指令时,我们需要它们。在这个例子中这不是大问题。

假设您想要将formGroup.controls.bookNames.at(0)移动到子组件中。使用formControl指令,您只需要@Input() formCtrl!: FormControl<string | null>;即可使其工作。完整代码如下所示:

@Component({
  selector: "app-item",
  imports: [FormsModule, ReactiveFormsModule],
  template: `
    <input
      [placeholder]="'please enter the book name'"
      [formControl]="formCtrl"
    />
  `,
  styles: ``,
})
export class ItemComponent {
  @Input() formCtrl!: FormControl<string | null>;
}

以及

<div>
  <input
    [placeholder]="'please enter the booker name'"
    [formControl]="formGroup.controls.booker"
  />
  <app-item [formCtrl]="formGroup.controls.bookNames.at(0)"> </app-item>
</div>

可以看到所需的努力和更改都很小。

而使用formControlName,则需要@Input() formGroup!: FormGroup<any>;@Input() formArrayName!: string;@Input() formCtrlName!: string;才能使其工作。完整代码如下:

@Component({
  selector: "app-item",
  imports: [FormsModule, ReactiveFormsModule],
  template: `
    <ng-container [formGroup]="formGroup">
      <ng-container [formArrayName]="formArrayName">
        <input
          [placeholder]="'please enter the book name'"
          [formControlName]="formCtrlName"
        />
      </ng-container>
    </ng-container>
  `,
  styles: ``,
})
export class ItemComponent {
  @Input() formGroup!: FormGroup<any>;
  @Input() formArrayName!: string;
  @Input() formCtrlName!: string;
}
<div [formGroup]="formGroup">
  <input
    [placeholder]="'please enter the booker name'"
    formControlName="booker"
  />
  <app-item
    [formCtrlName]="'0'"
    [formArrayName]="'bookNames'"
    [formGroup]="formGroup"
  >
  </app-item>
</div>

如您所见,这样做看起来并不聪明。

实际上,ItemComponent可以仅用formControlName工作,但在这种情况下,ItemComponent需要实现ControlValueAccessor,这不是很简单,并带来了额外的复杂性。