Angular2 FormBuilder Validatiors:要求填充组中的至少一个字段

时间:2016-10-29 16:10:47

标签: validation angular formbuilder

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

经过大量研究后,我遇到了解决这个问题的问题。我知道我可以使用其他方法来做到这一点,但我想知道是否可以使用FormBuilder验证器。如果我添加“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 }
                )

我花了一段时间,但关键是要添加关键字“验证器”,这将导致组验证器触发。

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

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

我希望这有助于其他任何人。

3 个答案:

答案 0 :(得分:26)

我使用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) })
}

答案 1 :(得分:8)

这是一个可以与每个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);
  }
}

答案 2 :(得分:0)

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

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;
  };
};