使用Angular 2,需要从两个字段中选择一个字段。

26

我正在尝试创建一个联系表单。该表单如下:

<form novalidate [formGroup]="contact" (ngSubmit)="send()">
  <p>
    <label>Name
      <br>
      <input type="text" class="input" value="" formControlName="name">
      <span class="error">Enter your name</span>
    </label>
  </p>
  <p>
    <label>E-mail
      <br>
      <input type="email" class="input" value="" formControlName="email">
      <span class="error">It looks like this email is invalid</span>
    </label>
  </p>
  <p>
    <label>Phone
      <br>
      <input type="text" class="input" value="" formControlName="telefone">
      <span class="error">It looks like this phone number is invalid</span>
    </label>
  </p>
  <p>
    <label>Message
      <br>
      <textarea type="text" class="input" value="" formControlName="message"></textarea>
      <span class="error">The message can't be empty</span>
    </label>
  </p>
  <p class="submit">
    <input type="submit" name="submit" class="bt" value="Send">
  </p>
</form>

在这个表单中,只有消息、姓名和电子邮件或电话号码字段应该是必填的。

我正在使用一个formBuilder类,所以这里是TypeScript代码:

this.contact = this.formBuilder.group({
  name: ['', Validators.required],
  email: ['', Validators.compose([/*Use custom validador??*/])],
  phone: ['', Validators.compose([/*Use custom validador??*/]],
  message: ['', Validators.required]
});

我尝试使用自定义验证器作为解决方案,但是我无法找到解决方法。有什么建议吗?

4个回答

34

我创建了一个自定义验证器指令:

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

  export const atLeastOne = (validator: ValidatorFn, controls:string[] = null) => (
    group: FormGroup,
  ): ValidationErrors | null => {
    if(!controls){
      controls = Object.keys(group.controls)
    }

    const hasAtLeastOne = group && group.controls && controls
      .some(k => !validator(group.controls[k]));

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

使用它很简单,只需这样做:

this.form = this.formBuilder.group({
            name: ['', Validators.required],
            email:[''],
            telefone:[''],
            message:['', Validators.required],
        }, { validator: atLeastOne(Validators.required, ['email','telefone']) });

所以这里需要电子邮件电话号码,如果您将其留空,则任何带有值的控件都可以使用,并且您可以将其与任何类型的验证器一起使用,而不仅仅是Validators.required

这可以在任何表单中重复使用。


16

是的,自定义验证器是正确的方法。

将您的表单组设置为以下内容:

this.contact = this.formBuilder.group({
  name: ['', Validators.required],
  email: ['', Validators.required],
  phone: ['', Validators.required],
  message: ['', Validators.required]
}, {validator: this.customValidationFunction})

然后使用customValidationFunction进行验证。这里只是举个例子,假设我们进行一项虚构的验证:

customValidationFunction(formGroup): any {
   let nameField = formGroup.controls['name'].value; //access any of your form fields like this
   return (nameField.length < 5) ? { nameLengthFive: true } : null;
}
将每个输入更改为以下内容(将p标签更改为div)。对于每个控件名称进行替换,并在适当的情况下更改隐藏的span标签验证语法:
<div [ngClass]="{'has-error':!contact.controls['name'].valid && contact.controls['name'].touched}">
    <label>Name</label>
    <input class="input" type="text" [formControl]="contact.controls['name']">
    <span [hidden]="!contact.hasError('nameLengthFive')" class="error">Enter your name</span>
</div>

1
要显示自定义错误nameLengthFive,请记住在FormGroup上调用hasError,而不是FormControl。正如所写的答案一样,您已经做对了,只是很容易忽略。 - Kent Bull

12

我已经更新了验证器代码片段,以支持字符串和数字类型。

我受到了Todd Skelton的启发。这是一个非常简单的验证器,它只做你要求的事情,没有其他多余的功能:

/**
 * Validates if at least one of the provided fields has a value.
 * Fields can only be of type number or string.
 * @param fields name of the form fields that should be checked
 */
export function atLeastOne(...fields: string[]) {
  return (fg: FormGroup): ValidationErrors | null => {
    return fields.some(fieldName => {
      const field = fg.get(fieldName).value;
      if (typeof field === 'number') return field && field >= 0 ? true : false;
      if (typeof field === 'string') return field && field.length > 0 ? true : false;
    })
      ? null
      : ({ atLeastOne: 'At least one field has to be provided.' } as ValidationErrors);
  };
}

以下是我如何使用它:

  ngOnInit(): void {
    this.form = this.formBuilder.group(
      {
        field: [this.field],
        anotherField: [this.anotherField],
      },
      { validator: atLeastOne('field','anotherField') },
    );
  }


2
你传递的函数命名错误(应该是atLeastOne,而不是atLeastOneOf)。 - Jefferson Hudson
对我来说,在 Angular 7 中它失败了,出现在这个部分 'field: [this.field]'。 - qleoz12

2

针对懒人稍微修改了Florian的回答,因为它让ts-sonar生气了(认知复杂度规则):

最初的回答:

const isFieldEmpty = (fieldName: string, fg: FormGroup) => {
    const field = fg.get(fieldName).value;
    if (typeof field === 'number') { return field && field >= 0 ? true : false; }
    if (typeof field === 'string') { return field && field.length > 0 ? true : false; }
};

export function atLeastOne(...fields: string[]) {
  return (fg: FormGroup): ValidationErrors | null => {
    return fields.some(fieldName => isFieldEmpty(fieldName, fg))
      ? null
      : ({ atLeastOne: 'At least one field has to be provided.' } as ValidationErrors);
  };
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接