如何使用第三方JS组件进行Angular2/4验证
当谈到Angular 2验证时,我看到了不同的方法。基本的方法是使用HTML5和模板/模型绑定,或者使用带有指定验证器的表单 然而,当涉及到特殊规则时,它需要大量的编码,并且使用模板绑定(因此没有表单)会导致验证分散在HTML和TS代码中 关于验证的另一个恼人的事情是,您需要在TS中更改验证规则,并添加额外的HTML代码以在页面上呈现这些值(尽管这可以通过指令自动实现) 无论如何,理想的解决方案是使用规则对属性建模,然后生成错误消息给控件。类似于.NET fluent验证和ModelState,或在JS world中类似于Aurelia.io验证:如何使用第三方JS组件进行Angular2/4验证,angular,validation,Angular,Validation,当谈到Angular 2验证时,我看到了不同的方法。基本的方法是使用HTML5和模板/模型绑定,或者使用带有指定验证器的表单 然而,当涉及到特殊规则时,它需要大量的编码,并且使用模板绑定(因此没有表单)会导致验证分散在HTML和TS代码中 关于验证的另一个恼人的事情是,您需要在TS中更改验证规则,并添加额外的HTML代码以在页面上呈现这些值(尽管这可以通过指令自动实现) 无论如何,理想的解决方案是使用规则对属性建模,然后生成错误消息给控件。类似于.NET fluent验证和ModelState,
控制
我们首先需要将表单控件链接到输入元素。它不是现成的。到目前为止,唯一的解决方案(至少对于纯ngModel绑定)是我发布的 下面的代码不适用于纯ngModel绑定,所以我做了很多实验。Maximillian Schwarzmuller也确认的最新版本应为:
@Directive({
selector: '[ngModel]', // or 'input, select, textarea' - but then your controls won't be handled and also checking for undefined would be necessary
})
export class NativeElementInjectorDirective {
constructor(private el: ElementRef, private control : NgControl) {
(<any>control.control).nativeElement = el.nativeElement;
}
}
@指令({
选择器:“[ngModel]”、//或“input,select,textarea”-但这样就不会处理控件,还需要检查未定义的控件
})
导出类NativeElementInjector指令{
构造函数(专用el:ElementRef,专用控件:NgControl){
(control.control).nativeElement=el.nativeElement;
}
}
因此,如果在主模块中提供并导出此指令,它将向所有FormControl附加一个自定义nativeElement属性下一步是了解如何正确构建嵌套表单-请参阅 而且不仅仅是前进ngForm,而且前进ngModelGroup:
@Component({
selector: 'TestAddress',
templateUrl: './address.location.html',
viewProviders: [
{ provide: ControlContainer, useExisting: NgForm },
{ provide: ControlContainer, useExisting: forwardRef(() => NgModelGroup) },
],
providers: [ MakeProvider(TestAddress) ]
})
export class TestProspectAddressLocation extends AbstractValueAccessor<any> {
...
}
现在,您可以订阅formValueChange和用户第三方进行验证,通过路径表达式访问任何控件,使用control.setErrors([])或通过jQuery aft.input添加HTML。仍在寻找:
import {Directive, OnInit, Optional, ViewContainerRef} from "@angular/core";
import {NgControl} from "@angular/forms";
import {ValidationContainerDirective} from "./validation-container-directive";
/*
This directive should match all inputs (ngModel)...
If it's inside a validation container, it'll process it...
*/
@Directive({
selector: '[ngModel]'
})
export class ValidationControlForInjectorDirective implements OnInit {
private initialized: boolean = false;
constructor(@Optional() private validationContainer: ValidationContainerDirective,
private control: NgControl,
private viewContainerRef: ViewContainerRef,
) {
// skip if not inside a validation container...
if (!this.validationContainer)
return;
// mark as initialized
this.initialized = true;
}
ngOnInit(): void {
// skip if not initialized
if (!this.initialized)
return;
var thisLocator = this.control.path.join(".");
// check inputs...
if (!thisLocator)
throw new Error('Validation wire issues!');
// add a control aft. the input...but disable it temporarily...
this.validationContainer.injectComponent(this.viewContainerRef, thisLocator);
}
}
injectComponent(componentVcRef: ViewContainerRef, locator: string) {
// create component
var componentRef = componentVcRef.createComponent(this.componentFactoryForValidationControlFor);
// basic set up (switching server/client messages on/off is done by the doCheckManagedComponents() fnc.
(<ValidationControlFor>componentRef.instance).locator = locator;
(<ValidationControlFor>componentRef.instance).isDynamic = true;
(<ValidationControlFor>componentRef.instance).serverMessages = false;
(<ValidationControlFor>componentRef.instance).clientMessages = false;
(<ValidationControlFor>componentRef.instance).clientMessagesAlwaysVisible = this.clientMessagesAlwaysVisible;
componentRef.changeDetectorRef.detectChanges();
// NOTE:
// registering of managed component is done via the component's ngOnInit function...
// ...it was moved there, because we need to keep track also of manually added validation-message-controls!
}
上面的injectComponent方法只是设置我的控件并将其附加到viewContainerRef:
import {Directive, OnInit, Optional, ViewContainerRef} from "@angular/core";
import {NgControl} from "@angular/forms";
import {ValidationContainerDirective} from "./validation-container-directive";
/*
This directive should match all inputs (ngModel)...
If it's inside a validation container, it'll process it...
*/
@Directive({
selector: '[ngModel]'
})
export class ValidationControlForInjectorDirective implements OnInit {
private initialized: boolean = false;
constructor(@Optional() private validationContainer: ValidationContainerDirective,
private control: NgControl,
private viewContainerRef: ViewContainerRef,
) {
// skip if not inside a validation container...
if (!this.validationContainer)
return;
// mark as initialized
this.initialized = true;
}
ngOnInit(): void {
// skip if not initialized
if (!this.initialized)
return;
var thisLocator = this.control.path.join(".");
// check inputs...
if (!thisLocator)
throw new Error('Validation wire issues!');
// add a control aft. the input...but disable it temporarily...
this.validationContainer.injectComponent(this.viewContainerRef, thisLocator);
}
}
injectComponent(componentVcRef: ViewContainerRef, locator: string) {
// create component
var componentRef = componentVcRef.createComponent(this.componentFactoryForValidationControlFor);
// basic set up (switching server/client messages on/off is done by the doCheckManagedComponents() fnc.
(<ValidationControlFor>componentRef.instance).locator = locator;
(<ValidationControlFor>componentRef.instance).isDynamic = true;
(<ValidationControlFor>componentRef.instance).serverMessages = false;
(<ValidationControlFor>componentRef.instance).clientMessages = false;
(<ValidationControlFor>componentRef.instance).clientMessagesAlwaysVisible = this.clientMessagesAlwaysVisible;
componentRef.changeDetectorRef.detectChanges();
// NOTE:
// registering of managed component is done via the component's ngOnInit function...
// ...it was moved there, because we need to keep track also of manually added validation-message-controls!
}
injectComponent(componentVcRef:ViewContainerRef,定位器:string){
//创建组件
var componentRef=componentVcRef.createComponent(this.componentFactoryForValidationControlFor);
//基本设置(打开/关闭服务器/客户端消息由doCheckManagedComponents()fnc完成。
(componentRef.instance).locator=定位器;
(componentRef.instance).isDynamic=true;
(componentRef.instance).serverMessages=false;
(componentRef.instance).clientMessages=false;
(componentRef.instance).clientMessagesAlwaysVisible=this.clientMessagesAlwaysVisible;
componentRef.changeDetectorRef.detectChanges();
//注:
//托管组件的注册通过组件的ngOnInit功能完成。。。
//…它被移到了那里,因为我们还需要跟踪手动添加的验证消息控件!
}
您可以尝试一个名为ts.validator.fluent.Generic object validation.fluent rules的框架/库
另外,一个演示框架的Angular 6 CLI应用程序:
/* Install npm package ts.validator.fluent and then import like below */
import { IValidator, Validator, ValidationResult } from 'ts.validator.fluent/dist';
/*TypeScript model*/
class Person {
Name: string;
}
/* Validation rules */
var validatePersonRules = (validator: IValidator<Person>) : ValidationResult => {
return validator
.NotEmpty(m => m.Name, "Name cannot be empty")
.ToResult();
};
/* Populate model */
var person = new Person();
person.Name = "Shane";
/* Validate model */
/* Sync */
var validationResult = new Validator(person).Validate(validatePersonRules);
/* Async */
var validationResult = await new Validator(person).ValidateAsync(validatePersonRules);
该应用程序使用面向服务的方法进行客户端表单验证,具有以下优点:
- 围绕验证的业务规则与组件分离
- 业务规则集中在验证服务中
- 该服务是可重用的
- 服务可以注入到任何组件中
- 您可以通过对服务进行单元测试来对业务规则进行单元测试
@Component({
selector: 'TestAddress',
templateUrl: './address.location.html',
viewProviders: [
{ provide: ControlContainer, useExisting: NgForm },
{ provide: ControlContainer, useExisting: forwardRef(() => NgModelGroup) },
],
providers: [ MakeProvider(TestAddress) ]
})
export class TestProspectAddressLocation extends AbstractValueAccessor<any> {
...
}
下面是如何使用框架验证TypeScript模型的示例:
/* Install npm package ts.validator.fluent and then import like below */
import { IValidator, Validator, ValidationResult } from 'ts.validator.fluent/dist';
/*TypeScript model*/
class Person {
Name: string;
}
/* Validation rules */
var validatePersonRules = (validator: IValidator<Person>) : ValidationResult => {
return validator
.NotEmpty(m => m.Name, "Name cannot be empty")
.ToResult();
};
/* Populate model */
var person = new Person();
person.Name = "Shane";
/* Validate model */
/* Sync */
var validationResult = new Validator(person).Validate(validatePersonRules);
/* Async */
var validationResult = await new Validator(person).ValidateAsync(validatePersonRules);
/*安装npm包ts.validator.fluent,然后按如下方式导入*/
从“ts.Validator.fluent/dist”导入{IValidator,Validator,ValidationResult};
/*类型脚本模型*/
班主任{
名称:字符串;
}
/*验证规则*/
var validatePersonRules=(验证器:IValidator):ValidationResult=>{
返回验证器
.NotEmpty(m=>m.Name,“Name不能为空”)
.ToResult();
};
/*填充模型*/
var person=新的person();
人,不