组件中的 Angular2 ngModel 验证器
Angular2 ngModel validator just in the component
我试图找出为 ngModel 实现自定义验证器逻辑的最简单方法。我有一个存储当前数据的预定义模型(接口),所以我不想处理新的 FormGroup/FormControl(模型驱动)方法。
如果我已经拥有我需要的所有数据,为什么还要使用 FormControls 构建完全相同的模式?
这是我的代码 (https://plnkr.co/edit/fPEdbMihRSVqQ5LZYBHO):
import { Component, Input } from '@angular/core';
export interface MyWidgetModel {
title:string;
description:string;
}
@Component({
selector: 'my-widget',
template: `
<h4 *ngIf="!editing">{{data.title}}</h4>
<input *ngIf="editing" type="text" name="title" [(ngModel)]="data.title">
<p *ngIf="!editing">{{data.description}}</p>
<textarea *ngIf="editing" name="description" [(ngModel)]="data.description" (ngModelChange)="customValidator($event)"></textarea>
<button (click)="clickEditing()">{{editing ? 'save' : 'edit'}}</button>
`
styles: [
':host, :host > * { display: block; margin: 5px; }',
':host { margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #eee; }',
'.ng-invalid { background-color: #FEE; }'
]
})
export class MyWidgetComponent {
@Input() data:MyWidgetModel;
constructor() {
this.editing = false;
}
clickEditing() {
this.editing = !this.editing;
}
customValidator(value:string) {
console.log(this, value); //should be: MyWidgetComponent
//How to set 'invalid' state here?
}
}
如您所见,我可以快速打开 on/off 编辑模式,我可以直接就地编辑我的数据。
我的问题是如何直接从我的组件管理 ng-valid/ng-invalid ngModel 的状态?这背后的想法包括多个点:
- 当数据模型已经存在时,为什么我们要为 FormGroups、FormControls 创建一个具有相同结构的新局部变量?
- 组件本身实现了所需的业务逻辑,因此所有业务规则验证器也必须在此处实现。
- 可以有很多复杂的验证逻辑。这些不能仅使用输入的纯文本值和简单检查(如必需、长度、模式等)来实现。
- 由于以上所有原因,我认为我们最终需要整个组件对象来解决所有实际业务规则验证。
终于想到办法了。我认为这是最简单的。
我还更新了插件:https://plnkr.co/edit/fPEdbMihRSVqQ5LZYBHO
让我们一步步来看。
1 - 创建一个实现 Validator 接口的简单、最小的指令 - 就像往常一样 - 但不要编写任何验证逻辑。而是提供一个函数类型的 Input() 字段——与选择器同名。这将允许我们在这个验证器之外实现真正的逻辑。在 validate(...) 函数内部调用外部 Input() 函数。
import { Directive, forwardRef, Input } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn } from '@angular/forms';
@Directive({
selector: '[myvalidator][ngModel],[myvalidator][ngFormControl]',
providers: [{
multi: true,
provide: NG_VALIDATORS,
useExisting: forwardRef(() => MyValidator)
}]
})
export class MyValidator implements Validator {
@Input() myvalidator:ValidatorFn; //same name as the selector
validate(control: AbstractControl):{ [key: string]: any; } {
return this.myvalidator(control);
}
}
2 - 要使用自定义验证器,只需将其导入并添加到组件的指令数组中。在模板标记中像使用任何其他指令一样使用它:
<input type="text" name="title" [(ngModel)]="data.title" [myvalidator]="validateTitle()">
诀窍就在这里。传递给验证器的 Input() 函数的值是一个函数调用 - 这将 returns 一个验证器函数。这是:
validateTitle() {
return <ValidatorFn>((control:FormControl) => {
//implement a custom validation logic here.
//the 'this' points the component instance here thanks to the arrow syntax.
return null; //null means: no error.
});
以上所有与官方 Angular2 验证器完全兼容 - 必需、模式等 - 因此我们的自定义验证器可以组合在一起而无需任何其他技巧。
编辑:
如果在组件的构造函数中为每次验证创建局部变量,则可以更简单有效地实现:
private validateTitle:ValidatorFn;
constructor() {
this.validateTitle = (control:FormControl) => {
//implement a custom validation logic here.
//the 'this' points the component instance here thanks to the arrow syntax.
return null; //null means: no error.
};
}
使用这种方法,我们只创建了一次 ValidatorFn 函数,而不是为每个验证请求创建一次。删除了 1 个函数调用:validateTitle()。所以在模板中我们可以绑定我们的变量:
<input type="text" name="title" [(ngModel)]="data.title" [myvalidator]="validateTitle">
如果您不想使用一次性模板驱动的表单验证指令:
使输入可访问
#receiverInput="ngModel"
在控制器中绑定
@ViewChild(NgModel, { static: true }) receiverInput: NgModel;
验证
this.receiverInput.control.setValidators((control: AbstractControl) => {
if (!this.receiver.kundenNr) {
// invalid
return { receiver: false };
}
// valid
return null;
});
我试图找出为 ngModel 实现自定义验证器逻辑的最简单方法。我有一个存储当前数据的预定义模型(接口),所以我不想处理新的 FormGroup/FormControl(模型驱动)方法。
如果我已经拥有我需要的所有数据,为什么还要使用 FormControls 构建完全相同的模式?
这是我的代码 (https://plnkr.co/edit/fPEdbMihRSVqQ5LZYBHO):
import { Component, Input } from '@angular/core';
export interface MyWidgetModel {
title:string;
description:string;
}
@Component({
selector: 'my-widget',
template: `
<h4 *ngIf="!editing">{{data.title}}</h4>
<input *ngIf="editing" type="text" name="title" [(ngModel)]="data.title">
<p *ngIf="!editing">{{data.description}}</p>
<textarea *ngIf="editing" name="description" [(ngModel)]="data.description" (ngModelChange)="customValidator($event)"></textarea>
<button (click)="clickEditing()">{{editing ? 'save' : 'edit'}}</button>
`
styles: [
':host, :host > * { display: block; margin: 5px; }',
':host { margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #eee; }',
'.ng-invalid { background-color: #FEE; }'
]
})
export class MyWidgetComponent {
@Input() data:MyWidgetModel;
constructor() {
this.editing = false;
}
clickEditing() {
this.editing = !this.editing;
}
customValidator(value:string) {
console.log(this, value); //should be: MyWidgetComponent
//How to set 'invalid' state here?
}
}
如您所见,我可以快速打开 on/off 编辑模式,我可以直接就地编辑我的数据。
我的问题是如何直接从我的组件管理 ng-valid/ng-invalid ngModel 的状态?这背后的想法包括多个点:
- 当数据模型已经存在时,为什么我们要为 FormGroups、FormControls 创建一个具有相同结构的新局部变量?
- 组件本身实现了所需的业务逻辑,因此所有业务规则验证器也必须在此处实现。
- 可以有很多复杂的验证逻辑。这些不能仅使用输入的纯文本值和简单检查(如必需、长度、模式等)来实现。
- 由于以上所有原因,我认为我们最终需要整个组件对象来解决所有实际业务规则验证。
终于想到办法了。我认为这是最简单的。 我还更新了插件:https://plnkr.co/edit/fPEdbMihRSVqQ5LZYBHO
让我们一步步来看。
1 - 创建一个实现 Validator 接口的简单、最小的指令 - 就像往常一样 - 但不要编写任何验证逻辑。而是提供一个函数类型的 Input() 字段——与选择器同名。这将允许我们在这个验证器之外实现真正的逻辑。在 validate(...) 函数内部调用外部 Input() 函数。
import { Directive, forwardRef, Input } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn } from '@angular/forms';
@Directive({
selector: '[myvalidator][ngModel],[myvalidator][ngFormControl]',
providers: [{
multi: true,
provide: NG_VALIDATORS,
useExisting: forwardRef(() => MyValidator)
}]
})
export class MyValidator implements Validator {
@Input() myvalidator:ValidatorFn; //same name as the selector
validate(control: AbstractControl):{ [key: string]: any; } {
return this.myvalidator(control);
}
}
2 - 要使用自定义验证器,只需将其导入并添加到组件的指令数组中。在模板标记中像使用任何其他指令一样使用它:
<input type="text" name="title" [(ngModel)]="data.title" [myvalidator]="validateTitle()">
诀窍就在这里。传递给验证器的 Input() 函数的值是一个函数调用 - 这将 returns 一个验证器函数。这是:
validateTitle() {
return <ValidatorFn>((control:FormControl) => {
//implement a custom validation logic here.
//the 'this' points the component instance here thanks to the arrow syntax.
return null; //null means: no error.
});
以上所有与官方 Angular2 验证器完全兼容 - 必需、模式等 - 因此我们的自定义验证器可以组合在一起而无需任何其他技巧。
编辑: 如果在组件的构造函数中为每次验证创建局部变量,则可以更简单有效地实现:
private validateTitle:ValidatorFn;
constructor() {
this.validateTitle = (control:FormControl) => {
//implement a custom validation logic here.
//the 'this' points the component instance here thanks to the arrow syntax.
return null; //null means: no error.
};
}
使用这种方法,我们只创建了一次 ValidatorFn 函数,而不是为每个验证请求创建一次。删除了 1 个函数调用:validateTitle()。所以在模板中我们可以绑定我们的变量:
<input type="text" name="title" [(ngModel)]="data.title" [myvalidator]="validateTitle">
如果您不想使用一次性模板驱动的表单验证指令:
使输入可访问
#receiverInput="ngModel"
在控制器中绑定
@ViewChild(NgModel, { static: true }) receiverInput: NgModel;
验证
this.receiverInput.control.setValidators((control: AbstractControl) => {
if (!this.receiver.kundenNr) {
// invalid
return { receiver: false };
}
// valid
return null;
});