【问题标题】:Angular2 FormBuilder Validators: require at least one field in a group to be filledAngular2 FormBuilder Validators:要求至少填写一个组中的一个字段
【发布时间】:2017-03-12 06:54:36
【问题描述】:

我有一个表格,用于收集电话号码(手机号码、个人号码、其他号码)。我需要至少填充输入。我正在尝试使用 Angular2 FormBuilder。

经过大量研究,我无法解决这个问题。我知道我可以使用其他方法来做到这一点,但我想知道是否可以使用 FormBuilder Validators。如果我添加“Validators.required”,那么所有 3 个字段都是必需的。有什么建议或想法吗?

phone: this._fb.group(
                    {
                        other: [''],
                        personal: [''],
                        mobile: [''],
                    }

根据“JB Nizet”的提示,我必须执行以下操作才能使其工作:

我的组验证器(仍然需要调整):

static phoneExists(group: FormGroup): { [key: string]: any } {

    if (null != group) {
        var other: AbstractControl = group.controls['other'];
        var mobile: AbstractControl = group.controls['mobile'];
        var personal: AbstractControl = group.controls['personal'];
        var data: Object = group.value;

        return (
            (other.valid && isEmptyInputValue(other.value))
            && (mobile.valid && isEmptyInputValue(mobile.value))
            && (personal.valid && isEmptyInputValue(personal.value))
            )
            ? { 'required': true }
            : null;
    }
}

我的组更改:

phone: this._fb.group(
                    {
                        other: [''],
                        personal: [''],
                        mobile: [''],
                    },
                    { validator: MyValidators.phoneExists }
                )

我花了一些时间,但关键是添加关键字“validator”,它会导致组验证器触发。

在 HTML 中我添加了以下内容:

<small *ngIf="!myForm.controls.profile.controls.phone.valid" class="text-danger">
                                        At least one phone is required.
                                    </small>

我希望这对其他人有帮助。

【问题讨论】:

  • 您需要在组本身上指定一个验证器,并且该验证器需要检查它的至少一个控件是否有值。
  • 这个tutorial 可能有用。
  • 感谢 JB Nizet 的提示。

标签: validation angular formbuilder


【解决方案1】:

这是一个通用代码,可用于每个 FormGroup:

export function AtLeastOneFieldValidator(group: FormGroup): {[key: string]: any} {
  let isAtLeastOne = false;
  if (group && group.controls) {
    for (const control in group.controls) {
      if (group.controls.hasOwnProperty(control) && group.controls[control].valid && group.controls[control].value) {
        isAtLeastOne = true;
        break;
      }
    }
  }
  return isAtLeastOne ? null : { 'required': true };
}

及用法:

@Component({
  selector: 'app-customers',
  templateUrl: './customers.component.html',
  styleUrls: ['./customers.component.scss']
})
export class CustomersComponent implements OnInit {

  public searchCustomerForm: FormGroup;

  constructor() { }

  ngOnInit() {
    this.searchCustomerForm = new FormGroup({
      customerID: new FormControl(''),
      customerEmail: new FormControl(''),
      customerFirstName: new FormControl(''),
      customerLastName: new FormControl('')
    }, AtLeastOneFieldValidator);
  }
}

【讨论】:

  • 谢谢,它帮助我验证了 formArray 是否包含至少一个孩子,稍微调整一下就完成了!
  • 很棒的概念,但是如果group.controls[control].value 是 0 或假怎么办?我会考虑放弃价值检查。
【解决方案2】:

我使用atLeastOne 函数基于任何现有验证器创建自定义验证器:

import { FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms';

export const atLeastOne = (validator: ValidatorFn) => (
  group: FormGroup,
): ValidationErrors | null => {
  const hasAtLeastOne =
    group &&
    group.controls &&
    Object.keys(group.controls).some(k => !validator(group.controls[k]));

  return hasAtLeastOne ? null : { atLeastOne: true };
};

美妙之处在于您可以使用任何验证器,而不仅仅是Validators.required

在 OP 的情况下,它将像这样使用:

{
  phone: this._fb.group({
    other: [''],
    personal: [''],
    mobile: [''],
  }, { validator: atLeastOne(Validators.required) })
}

【讨论】:

  • 纯粹的天才,我喜欢它!感谢分享 :) ...虽然 'ValidatorFn' 和 'ValidationErrors' 是什么 - 我需要导入它们吗?
  • 他们是@angular/forms 的一部分。编辑答案。不客气。很高兴它有帮助:-)
  • 是的,但是当您希望至少在两个字段之间时?
【解决方案3】:

这是没有子组的表单的方法。它允许有一个字段验证器,而不是一个组。

const atLeastOneList = ['field2', 'field4', 'field5'];
this.form = this.fb.group({
  field1: [''],
  field2: ['', this.requiredAtLeastOne(atLeastOneList)],
  field3: [''],
  field4: ['', this.requiredAtLeastOne(atLeastOneList)],
  field5: ['', this.requiredAtLeastOne(atLeastOneList)],
});

方法实现应该包含一个隐式保护 “超出最大调用堆栈大小”错误,因为我们要重新验证字段,我们需要避免递归。

requiredAtLeastOne(fields: string[]) {
  return (control: FormControl) => {
    // check if at least one field is set
    const result = fields.some(name => {
      const ctrl = control.parent.get(name);
      return ctrl && ctrl.value && ctrl.valid;
    });
    // run at-least-one validator for other fields
    Object.entries(control.parent.controls)
      .filter(([name, ctrl]) =>
        // here we are, proper filter prevents stack overflow issue
        fields.includes(name) && ctrl !== control && !ctrl.valid && result
      )
      .forEach(([, ctrl]) => ctrl.updateValueAndValidity())

    return !result ? { requiredAtLeastOne: true } : null;
  };
};

【讨论】:

    【解决方案4】:

    接受的答案是正确的,但是,您会在最新的 Angular 版本中收到折旧警告。所以,对于较新的版本,试试这个:

    employee: this.fb.group({
        FirstName: [null],
        LastName: [null],
        Dob: [null],
      }, { validators: atLeastOne(Validators.required, ["FirstName", "LastName"]) }),
    

    验证:

    export function atLeastOne(validator: ValidatorFn, controls: string[] = []): ValidatorFn {
      return (control: AbstractControl): ValidationErrors | null => {
        if (!control) return null;
        const formGroup = control as FormGroup;
        return (formGroup && controls.some(k => !validator(formGroup.controls[k]))) ? null : {
          atLeastOne: true,
        };
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2010-11-21
      • 1970-01-01
      • 2013-01-05
      • 2020-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-21
      相关资源
      最近更新 更多