具有多个字段的角度模板驱动表单验证

时间:2018-05-16 19:50:55

标签: angular angular-forms angular-validation

假设我有一些带有一些字段(Stackblitz example)的简单表格:

@Component({
  selector: 'my-app',
  template: 
`
<h1>AppComponent</h1>

<form>
  <h2>UserData</h2>
  <userdata [user]="model.userData"></userdata>

  <h2>Actions</h2>
  <actionbar ></actionbar>
</form>
`,
})
export class AppComponent  { ... }

@Component({
  selector: 'userdata',
  template: 
`
<span class="status {{name.status}}">{{name.status}}</span>
full name:
<input name="name" #name="ngModel" pattern="^.* .*$" required [(ngModel)]="user.name">
<br>

<h3>--- Contacts ---</h3>

<span class="status {{email.status}}">{{email.status}}</span>
email:
<input name="email" #email="ngModel" type="email" [email]="true" [(ngModel)]="user.contacts.email">
<br>


<span class="status {{phone.status}}">{{phone.status}}</span>
phone:
<input name="phone" #phone="ngModel" pattern="^[0-9]*$" [(ngModel)]="user.contacts.phone">
<br>


<h4>---- Address ----</h4>

<span class="status {{street.status}}">{{street.status}}</span>
street:
<input name="street" #street="ngModel" [(ngModel)]="user.contacts.address.street">
<br>

<span class="status {{city.status}}">{{city.status}}</span>
city:
<input name="city" #city="ngModel" [(ngModel)]="user.contacts.address.city">
<br>

<span class="status {{zipcode.status}}">{{zipcode.status}}</span>
zipcode:
<input name="zipcode" #zipcode="ngModel" pattern="^[0-9]{5}$" [(ngModel)]="user.contacts.address.zipcode">
<br>

`,
  viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
})
export class UserDataComponent  {
  @Input() user: any;
}

@Component({
  selector: 'actionbar',
  template: 
`
<span class="status {{form.status}}">{{form.status}}</span>
<input type="button" value="Submit" [disabled]="form.invalid">
`,
  viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
})
export class ActionBarComponent { ... }

(基本上是一个包含全名和联系信息字段的表单,如电子邮件,电话,地址)

请注意,如果表单有效,则只能提交表单。只有当嵌套在其中的所有内容都有效时,表单才有效。

对于单个字段的验证,它很容易做到(这些字段已经有一些,如正则表达式模式和要求)。

现在我想添加其他两个业务要求:

  1. 至少需要一个联系信息(电子邮件,电话或地址);

  2. 如果设置了地址中的任何字段,则需要所有字段(街道,城市,邮政编码)。

  3. 甚至可以用模板驱动的形式进行吗?

2 个答案:

答案 0 :(得分:2)

这就是我设法实现分组验证的方式(Stackblitz Example)。

(对于我在下面粘贴的片段,括号中的省略号表示为了清晰起见省略了部分;请参阅上面的stackblitz示例获取完整内容)

对于每个组,我添加了一个包装元素(我认为它也可能是<ng-container>,虽然没有测试它)只是为了承载ngModelGroup指令:

<div ngModelGroup="contacts" (...) >

<h3>--- Contacts ---</h3>

email: (...input...)
phone: (...input...)

<div ngModelGroup="address" (...) >

<h4>---- Address ----</h4>

street: (...input...)
city: (...input...)
zipcode: (...input...)

</div>

</div>

现在,每个新ngModelGroup都可以附加验证程序。由于这些验证是如此临时,我觉得它们不值得一个真正可重用的实现,我只需要验证函数(在这里粘贴其中一个;另一个非常直接,你总是可以参考stackblitz ):

  ifOneThenFullAddress(c: AbstractControl): ValidationErrors | null {
    let value = c.value;

    let street = value && value.street;
    let city = value && value.city;
    let zipcode = value && value.zipcode;

    if ((street && city && zipcode) || (!street && !city && !zipcode))
      return null;

    return { ifOneThenAll: '' };
  }

(此代码在UserDataComponent

中实现

现在要使角形式引擎调用我的函数,我必须实现一个Validator,但是一个通用的(将验证交给函数):

@Directive({
  selector: '[fn-validate]',
  providers: [{provide: NG_VALIDATORS, useExisting: FnValidateDirective, multi: true}]
})
export class FnValidateDirective implements Validator {
  @Input('fn-validate') fn: (c: AbstractControl) =>  ValidationErrors | null;

  validate(c: AbstractControl): ValidationErrors | null {
    return this.fn(c);
  }
}

要使用它(并绑定我的验证函数),我必须将组的元素更改为:

<div ngModelGroup="contacts" [fn-validate]="atLeastOneContact">
<div ngModelGroup="address" [fn-validate]="ifOneThenFullAddress">

而且,整个团队得到了我的临时职能部门的验证。

答案 1 :(得分:0)

这是可能的,但是由于代码变得模糊不清,所以它是不切实际的。

条件1:

<input [required]=!(somefielf1 || somefield2 || somefield3 ... etc )>

并且对于每个输入必须满足不同的条件。如果没有输入任何输入,则需要输入。

条件2:

这里只是相反 - 没有否定

<input name="city" [required]="zipCode || street || whatever else|| ..."/>
如果提供的字段之一不为空,则需要

输入。

使用反应形式,这样做会容易得多。条件将大致相同,但更容易阅读,维护和更改。