Angular 表单模式切换

Angular form mode toggling

假设我们有一个非常基本的 angular 响应式表单,具有两种模式:编辑和显示。在显示模式下输入字段被禁用,在编辑模式下启用:

<form [formGroup]="formGroup" (ngSubmit)="submitForm()" novalidate>
      <label for="fname">First name:</label><br />
      <input
        type="text"
        id="fname"
        name="fname"
        formControlName="fname"
      /><br />
      <label for="lname">Last name:</label><br />
      <input
        type="text"
        id="lname"
        name="lname"
        formControlName="lname"
      /><br /><br />
      <button type="submit">{{formMode == 'show' ? 'Edit' : 'Submit'}}</button>
    </form>
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  formGroup: FormGroup;
  formMode: 'show' | 'edit' = 'show';

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.formGroup = this.fb.group({
      fname: [{ value: 'John', disabled: this.formMode == 'show' }],
      lname: [{ value: 'Doe', disabled: this.formMode == 'show' }],
    });
  }

  submitForm() {
    if (this.formMode === 'edit') {
      console.log(this.formGroup.value);
      this.formMode = "show"
    } else {
      this.formMode = "edit"
    }
  }

然而,当 formMode 的状态从显示切换到编辑状态时,formControls 的禁用状态不会改变。

我知道我可以使用 this.formGroup.enable() 和 this.formGroup.disable(),但是如果有一个很大的表单并且只有一些字段在切换状态,那会是一团糟。

我预计这个禁用的道具是反应性的,因为 this.formMode 默认情况下是反应性的...

这是闪电战: https://stackblitz.com/edit/angular-ivy-4zybd7?file=src/app/app.component.ts

我们可以创建自定义指令并将其应用于表单元素,然后在指令中注入 formGroup 指令以获取所有 formControl 路径,然后根据我们的条件可以禁用或启用。

formControlPath有什么用?

FormGroup 可以有多个 formControl.To 使用 this.control.directives.map((dir) => dir.path.join())

获取所有 formControl 名称作为数组

为什么要使用 Promise.resolve?

如果你console.log(this.formControlPath)里面的 ngOnChanges 会是空的。要在 ngOnChanges 中获取所有已注册的 formControl 键,请使用 setTimeOut 或 Promise.resolve.

custom.directive.ts

import { Directive, Input, OnChanges, OnInit } from '@angular/core';
import { FormGroupDirective, NgControl } from '@angular/forms';

export type FormMode = 'show' | 'edit';

@Directive({
  selector: '[appCanDisable]',
})
export class CanDisableDirective implements OnChanges {
  @Input() formMode: FormMode;
  @Input() disableFields: string[]; //Field you want to disable pass it from parent compinent
  get formControlPath(): string[] {
    return this.control.directives.map((dir) => dir.path.join());
  }
  constructor(private control: FormGroupDirective) {}
  ngOnChanges() {
    Promise.resolve().then(() => {
      const state = this.formMode === 'show' ? 'disable' : 'enable';
      this.formControlPath.forEach((path) => {
        if (this.disableFields.find((field) => field === path)) {
          this.control.form.get(path)[state]();
        }
      });
    });
  }
}

Working Example