Angular 2 -警告/提示的表单验证



我试图添加表单验证,这不会使表单无效。验证应该只以警告的形式出现。

。年龄验证。年龄大于90表示警告,年龄大于120表示错误。

我在一个表单上尝试了两个FormGroups,在输入字段上尝试了两个[formControl]。只使用第一个[formControl]。

是否可以使用angular的表单验证来进行这种验证?哪一种方法才是正确的?

我通过创建始终返回null的自定义验证器来完成它。这个验证器还创建了额外的属性warnings。然后在视图中检查这个属性。

export interface AbstractControlWarn extends AbstractControl { warnings: any; }
export function tooBigAgeWarning(c: AbstractControlWarn) {
  if (!c.value) { return null; }
  let val = +c.value;
  c.warnings = val > 90 && val <= 120 ? { tooBigAge: {val} } : null;
  return null;
}
export function impossibleAgeValidator(c: AbstractControl) {
  if (tooBigAgeWarning(c) !== null) { return null; }
  let val = +c.value;
  return val > 120 ? { impossibleAge: {val} } : null;
}
@Component({
  selector: 'my-app',
  template: `
    <div [formGroup]="form">
      Age: <input formControlName="age"/>
      <div *ngIf="age.errors?.required" [hidden]="age.pristine">
      Error! Age is required
      </div>
      <div *ngIf="age.errors?.impossibleAge" [hidden]="age.pristine">
      Error! Age is greater then 120
      </div>
      <div *ngIf="age.warnings?.tooBigAge" [hidden]="age.pristine">
      Warning! Age is greater then 90
      </div>
      <p><button type=button [disabled]="!form.valid">Send</button>
    </div>
  `,
})
export class App {
  age: FormControl;
  constructor(private _fb: FormBuilder) { }
  ngOnInit() {
    this.form = this._fb.group({
      age: ['', [
        Validators.required,
        tooBigAgeWarning,
        impossibleAgeValidator]]
    })
    this.age = this.form.get("age");
  }
}

示例:https://plnkr.co/edit/me0pHePkcM5xPQ7nzJwZ?p=preview

Sergey Voronezhskiy的公认答案在开发模式下工作完美,但如果您尝试在-prod模式下构建,您将得到此错误。

...  Property 'warnings' does not exist on type 'FormControl'.

为了修复这个错误,我对原始代码(App类)进行了调整,基本上是修复松散类型的变量。这是新版本:

export class FormControlWarn extends FormControl { warnings: any; }
export function tooBigAgeWarning(c: FormControlWarn ) {
    if (!c.value) { return null; }
    let val = +c.value;
    c.warnings = val > 90 && val <= 120 ? { tooBigAge: {val} } : null;
    return null;
}
export function impossibleAgeValidator(c: AbstractControl) {
   if (tooBigAgeWarning(c) !== null) { return null; }
   let val = +c.value;
   return val > 120 ? { impossibleAge: {val} } : null;
}
@Component({
  selector: 'my-app',
  template: `
    <div [formGroup]="form">
      Age: <input formControlName="age"/>
      <div *ngIf="age.errors?.required" [hidden]="age.pristine">
      Error! Age is required
      </div>
      <div *ngIf="age.errors?.impossibleAge" [hidden]="age.pristine">
      Error! Age is greater then 120
      </div>
      <div *ngIf="age.warnings?.tooBigAge" [hidden]="age.pristine">
      Warning! Age is greater then 90
      </div>
      <p><button type=button [disabled]="!form.valid">Send</button>
    </div>
  `,
})
    export class App {
      get age(): FormControlWarn{
         return <FormControlWarn>this.form.get("age");
      }
      constructor(private _fb: FormBuilder) { }
      ngOnInit() {
        this.form = this._fb.group({
          age: new FormControlWarn('', [
            Validators.required,
            tooBigAgeWarning,
            impossibleAgeValidator])
        });       
      }
    }

我可能会这样做。

<form #form="ngForm" (ngSubmit)="save()">
  <input formControlName="controlName">
  <span *ngIf="form.pristine && form.controls.controlName.value > 90 && form.controls.controlName.value < 120">
   Warning: Age limit is high..
  </span>
  <span *ngIf="form.pristine && form.controls.controlName.value > 120">
   Error: Age limit exceeded..
  </span>
<form>

Ok

它可以很容易地通过角。IO的表单验证提示您可以在https://angular.io/docs/ts/latest/cookbook/form-validation.html

上阅读文档

但同样的方法可以帮助你更好的可能在我的脑海里。

首先我们创建一个抽象类Form,它包含一些常用的函数和属性。

import {FormGroup} from "@angular/forms";
export abstract class Form {
    form: FormGroup;
    protected abstract formErrors: Object;
    protected abstract validationMessages: Object;
    onValueChanged(data?: any) {
        if (!this.form) { return; }
        const form = this.form;
        for (const field in this.formErrors) {
            this.formErrors[field] = '';
            const control = form.get(field);
            if (control && control.dirty && !control.valid) {
                const messages = this.validationMessages[field];
                for (const key in control.errors) {
                    this.formErrors[field] = messages[key];
                    break;
                }
            }
        }
    }
}

,那么你应该创建一个表单组件,例如命名为LoginComponent,如下所示

import {Component, OnInit} from "@angular/core";
import {Form} from "./form";
import {Validators, FormBuilder} from "@angular/forms";

@Component({
    templateUrl: '...'
})
export class LoginComponent extends Form implements OnInit {
    protected formErrors = {
        'email': '',
        'password': ''
    }
    protected validationMessages = {
        'email': {
            'required': 'email required message',
            'email':  'email validation message'
        },
        'password': {
            'required': 'password required message',
            'minlength': 'password min length message',
            'maxlength': 'password max length message',
        }
    }
    constructor(private _fb: FormBuilder) { }
    ngOnInit() {
        this.buildForm();
    }
    buildForm() {
        this.form = this._fb.group({
            'email': ['', [
                Validators.required,
                // emailValidator
            ]],
            'password': ['', [
                Validators.required,
                Validators.minLength(8),
                Validators.maxLength(30)
            ]]
        });
        this.form.valueChanges
            .subscribe(data => this.onValueChanged(data));
        this.onValueChanged(); //
    }
}

首先,我们应该为响应式表单注入FormBuilder(不要忘记在主模块中导入ReactiveFormModule),然后在[buildForm()]方法中,我们构建了一组form on form属性,这些属性继承自抽象类form。

然后在接下来我们创建一个订阅表单值的变化和值的变化,我们调用[onValueChanged()]方法。

在[onValueChanged()]方法中,我们检查表单的字段是否有效,如果不是,我们从protected validationMessages属性中获取消息并将其显示在formErrors属性中。

那么你的模板应该类似于

<div class="col-md-4 col-md-offset-4">
    <form [formGroup]="form" novalidate>

        <div class="form-group">
            <label class="control-label" for="email">email</label>
            <input type="email" formControlName="email" id="email" class="form-control" required>
            <div class="help help-block" *ngIf="formErrors.email">
                <p>{{ formErrors.email }}</p>
            </div>
        </div>
        <div class="form-group">
            <label class="control-label" for="password">password</label>
            <input type="password" formControlName="password" id="password" class="form-control" required>
            <div class="help help-block" *ngIf="formErrors.password">
                <p>{{ formErrors.password }}</p>
            </div>
        </div>
        <div class="form-group">
            <button type="submit" class="btn btn-block btn-primary" [disabled]="!form.valid">login</button>
        </div>
    </form>
</div>

模板是如此简单,内部你检查字段是否有错误,如果有错误绑定。

对于bootstrap,你可以在 下面设置
<div class="form-group" [ngClass]="{'has-error': form.controls['email'].dirty && !form.controls['email'].valid, 'has-success': form.controls['email'].valid}">
    <label class="control-label" for="email">email</label>
    <input type="email"
           formControlName="email"
           id="email"
           class="form-control"
           required>
    <div class="help help-block" *ngIf="formErrors.email">
        <p>{{ formErrors.email }}</p>
    </div>
</div>

UPDATE:我试图为你创建一个非常简单但几乎完整的样本,当然你可以为更大的规模开发它:https://embed.plnkr.co/ExRUOtSrJV9VQfsRfkkJ/

但是这里有一些描述,你可以创建一个自定义验证,像下面的

import {ValidatorFn, AbstractControl} from '@angular/forms';
function isEmptyInputValue(value: any) {
  return value == null || typeof value === 'string' && value.length === 0;
}
export class MQValidators {
  static age(max: number, validatorName: string = 'age'): ValidatorFn {
    return (control: AbstractControl): {[key: string]: any} => {
      if (isEmptyInputValue(control.value)) return null;
      const value = typeof control.value == 'number' ? control.value : parseInt(control.value);
      if (isNaN(value)) return null;
      if (value <= max) return null;
      let result = {};
      result[validatorName] = {valid: false};
      return result;
    }
  }
}

这个自定义验证器有一个可选的参数名为validatorName,这个参数可以让你指定多个类似的验证,就像你的例子中的formComponent应该是这样的:

buildForm () {
    this.form = this._fb.group({
        'age': ['', [
            Validators.required,
            Validators.pattern('[0-9]*'),
            MQValidators.age(90, 'abnormalAge'),
            MQValidators.age(120, 'incredibleAge')
        ]]
    });
    this.form.valueChanges
        .subscribe(data => this.onValueChanged(data));
    this.onValueChanged();
}
onValueChanged(data?: any): void {
    if (!this.form) { return; }
    const form = this.form;
    for (const field in this.formErrors) {
        this.formErrors[field] = '';
        const control = form.get(field);
        if (control && control.dirty && !control.valid) {
            const messages = this.validationMessages[field];
            for (const key in control.errors) {
                this.formErrors[field] = messages[key];
            }
        }
    }
}
formErrors = {
    age: ''
}
validationMessages = {
    'age': {
        'required': 'age is required.',
        'pattern': 'age should be integer.',
        'abnormalAge': 'age higher than 90 is abnormal !!!',
        'incredibleAge': 'age higher than 120 is incredible !!!'
    }
}

希望我帮到你。

Sergey Voronezhskiy的回答非常精彩。下面是进一步抽象它并创建包装器的方法:

export interface ISpecialAbstractControl extends AbstractControl {
  warnings: any;
}
function warn(validator: ValidatorFn): ValidatorFn {
  return (c: ISpecialAbstractControl): ValidationErrors | null => {
      let result = validator(c);
      c.warnings = result;
      return null;
  };
}
formBuilder.group({
  'name': ['', { validators: [ warn(Validators.required) ] } ]
})
// you can access the warnings in code easily
Reflect.get(theFormField, 'warnings')
// or cast it to the ISpecialAbstractControl
(<ISpecialAbstractControl>formField).warnings

最新更新