如何使用第三方JS组件进行Angular2 / 4验证

时间:2018-01-10 15:09:27

标签: angular validation

当谈到Angular 2验证时,我看到了不同的方法。基本是使用HTML5和模板/模型绑定或具有指定验证器的表单。

然而,当涉及特殊规则时,它需要大量编码和模板绑定(因此没有形式),这主要导致验证通过HTML和TS代码分散。

验证的另一个烦人的事情是你需要改变TS中的验证规则,并添加额外的HTML代码以在页面上呈现这些值(虽然这可以通过指令自动化)...

无论如何,理想的解决方案是使用规则对属性进行建模,然后向控件生成错误消息。与.NET流畅验证和ModelState类似,或者在JS世界中类似于Aurelia.io验证:http://aurelia.io/docs/plugins/validation/

  1. Angular2 +中有类似的东西吗?
  2. 如何/在哪里检查(基于模型更改)到第三方(JS)库?像fluent-ts-validator或flient-validator一样。
  3. 如何设置控件上的错误,尤其是当我们有f.e.控制内部
    控制...

5 个答案:

答案 0 :(得分:0)

我们首先需要将FormControls链接到输入元素。它不是开箱即用的。到目前为止唯一的解决方案(至少对于纯ngModel绑定)是我发布的here

下面的代码不适用于纯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;
    }
}

因此,如果在主模块中提供并导出此指令,它会将自定义nativeElement属性附加到所有FormControl

答案 1 :(得分:0)

接下来的事情是知道如何正确构建嵌套表单 - 请参阅link here

此外,不仅仅是转发 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> {
...
}

我们必须使用 AbstractValueAccessor ,因此也使用MakeProvider。 Here's the link

最后一篇:一种访问这些FormControls的方法:

this.form.get('HomeAddress.City').nativeElement

现在您可以订阅formValueChange和用户第三方进行验证,通过路径表达式访问任何控件,使用control.setErrors([])或只是通过jQuery aft附加HTML。输入

答案 2 :(得分:0)

仍在寻找:

  1. 创建一些指令,可以为每个具有ngModel属性和名称定义的控件添加[(ngModel)]绑定 - 而指定的表达式应为&#39; value。&#39; +名称属性值
  2. 使用角度组件动态替换jQuery添加的div的方法 - 由于div是动态的,因此无法使用@ViewChild(选择器)
  3. 使用带有[(ngModel)]绑定的嵌套表单会产生一个奇怪的FormGroup结构,因此自定义角度组件应该自动定义ngModelGroup =&#39; name attr。值&#39;或者单独或以其他方式使用ngControlOptions应该是foud,这不会弄得一团糟......

答案 3 :(得分:0)

因此可以执行上面列表中的#2。

我们确实使用ngModel选择器来获取引用。对于所有输入,我们当然都有一个主控件(其中包含与验证相关的消息/代码),因此我们忽略不在该容器内的控件。

现在就来了窍门::我们确实获得了对输入本身的viewContainerRef。这使我们可以在输入后立即插入控件。由于注入是针对给定的viewContainer的,因此当隐藏输入get时,注入的控件将被破坏...

代码:

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方法只是设置了我的控件并将其附加到viewContainerRef:

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!
}

答案 4 :(得分:0)

您可以尝试一个名为 ts.validator.fluent 的框架/库。通用对象验证。流利的规则。

https://github.com/VeritasSoftware/ts.validator

还有一个 Angular 6 CLI应用来演示该框架:

https://github.com/VeritasSoftware/ts-validator-app-angular6

该应用程序使用面向服务的方法来进行客户端表单验证,具有以下优点:

  • 围绕验证的业务规则与组件分开。
  • 业务规则集中在验证服务中。
  • 该服务可重复使用。
  • 该服务可以注入到任何组件中。
  • 您可以通过对服务进行单元测试来对业务规则进行单元测试。

NPM软件包

https://www.npmjs.com/package/ts.validator.fluent

以下是使用框架验证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);