我试图添加表单验证,这不会使表单无效。验证应该只以警告的形式出现。
。年龄验证。年龄大于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