如何基于异步条件动态构建 Angular FormGroup 并调整其验证器

问题描述 投票:0回答:1

我正在 Angular 中开发一个表单,我需要根据通过效果异步检索的条件动态构建 FormGroup。条件 (sameServices) 确定是否应在表单中包含附加字段 addressComplement 并且是必需的。

这是我的代码:

constructor(private shared: Shared) {
  effect(() => {
    this.business = this.shared.getBusiness();
  });

  if (this.business.sameServices) {
    this.branchDataForm = new FormGroup({
      address: new FormControl('', [Validators.required, Validators.minLength(3)]),
      addressComplement: new FormControl('', [Validators.required, Validators.minLength(3)]),
    });
  } else {
    this.branchDataForm = new FormGroup({
      address: new FormControl('', [Validators.required, Validators.minLength(3)])
    });
  }
}

我的问题是:

构造函数在执行条件之前不会等待效果完成。

如果我将条件放在效果内,则 HTML 会失败,表明它无法找到控件,因为它们在模板渲染时尚未定义。

仅当必填字段存在并且基于条件(sameServices)满足其验证规则时,我才需要表单有效。

问题

基于异步条件,动态添加或删除控件及其验证器,在 Angular 中构建 FormGroup 的干净而有效的方法是什么? Angular 中是否有处理此类场景的最佳实践?

angular forms
1个回答
0
投票

您可以尝试这种方法,首先我们在初始加载期间设置所有可能的控件,但我们确保所有这些控件都是

disabled
,以便表单不会验证它们。

branchDataForm = new FormGroup({
  address: new FormControl({ value: '', disabled: true }, [
    Validators.required,
    Validators.minLength(3),
  ]),
  addressComplement: new FormControl({ value: '', disabled: true }, [
    Validators.required,
    Validators.minLength(3),
  ]),
});

我们使用

[hidden]
属性,而不是在反应式表单中效果很好的
ngIf
,我们可以使用这种逻辑来隐藏隐藏字段。

<form [formGroup]="branchDataForm">
  <input [hidden]="!showFields['address']" formControlName="address"/>    <br/><br/>
  <input [hidden]="!showFields['addressComplement']" formControlName="addressComplement"/><br/><br/>
</form>

信号完成后,我们只需使用以下函数启用控制即可。我们还设置隐藏/显示字段的布尔值。

enableFormFields(enableFields: Array<string>) {
  this.branchDataForm.disable();
  Object.keys(this.showFields).forEach((key: string) => {
    this.showFields[key] = false;
  });
  enableFields.forEach((controlName: string) => {
    const ctrl = this.branchDataForm.get(controlName);
    ctrl?.enable();
    this.showFields[controlName] = true;
  });
}

constructor() {
  effect(() => {
    this.business = this.getBusiness();
    if (this.business.sameServices) {
      this.enableFormFields(['address', 'addressComplement']);
    } else {
      this.enableFormFields(['address']);
    }
  });
}

完整代码:

import { Component, effect } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import {
  FormGroup,
  ReactiveFormsModule,
  FormControl,
  Validators,
} from '@angular/forms';
import { of, delay } from 'rxjs';
import { toSignal } from '@angular/core/rxjs-interop';
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="branchDataForm">
      <input [hidden]="!showFields['address']" formControlName="address"/>    <br/><br/>
      <input [hidden]="!showFields['addressComplement']" formControlName="addressComplement"/><br/><br/>
    </form>
  `,
})
export class App {
  getBusiness = toSignal(
    of({
      sameServices: false,
    })
  );
  business: any;
  branchDataForm = new FormGroup({
    address: new FormControl({ value: '', disabled: true }, [
      Validators.required,
      Validators.minLength(3),
    ]),
    addressComplement: new FormControl({ value: '', disabled: true }, [
      Validators.required,
      Validators.minLength(3),
    ]),
  });
  noAddress = true;
  showFields: any = {};

  enableFormFields(enableFields: Array<string>) {
    this.branchDataForm.disable();
    Object.keys(this.showFields).forEach((key: string) => {
      this.showFields[key] = false;
    });
    enableFields.forEach((controlName: string) => {
      const ctrl = this.branchDataForm.get(controlName);
      ctrl?.enable();
      this.showFields[controlName] = true;
    });
  }

  constructor() {
    effect(() => {
      this.business = this.getBusiness();
      if (this.business.sameServices) {
        this.enableFormFields(['address', 'addressComplement']);
      } else {
        this.enableFormFields(['address']);
      }
    });
  }
}

bootstrapApplication(App);

Stackblitz 演示

© www.soinside.com 2019 - 2024. All rights reserved.