【问题标题】:Angular2 ngModel validator just in the componentAngular2 ngModel 验证器就在组件中
【发布时间】:2016-12-12 22:00:26
【问题描述】:

我试图找出为 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?
  }

}

如您所见,我可以快速打开/关闭编辑模式,并且可以直接就地编辑我的数据。

我的问题是如何直接从我的组件管理 ngModel 的 ng-valid/ng-invalid 状态?这背后的想法包括多点:

  • 当数据模型已经存在时,我们为什么要为 FormGroups、FormControls 创建一个具有相同结构的新局部变量?
  • 组件本身实现了所需的业务逻辑,因此所有业务规则验证器也必须在这里实现。
  • 可能有许多复杂的验证逻辑。这些不能仅使用输入的纯文本值和简单的检查(如要求、长度、模式等)来实现。
  • 由于以上所有,我认为我们最终需要我们的整个组件对象来解决所有实际的业务规则验证。

【问题讨论】:

    标签: validation angular angular2-forms


    【解决方案1】:

    如果您不想要一次性模板驱动的表单验证指令:

    使输入可访问

    #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'”我应该导入什么?
    • @kmarakrout import { NgModel } from '@angular/forms';
    【解决方案2】:

    终于想出了一个办法。我认为这是最简单的。 我还更新了plunker: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() 函数的值是一个函数调用——它将返回一个验证器函数。这里是:

    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">
    

    【讨论】:

      猜你喜欢
      • 2016-05-24
      • 2017-12-21
      • 1970-01-01
      • 2016-11-30
      • 2017-05-12
      • 2017-04-01
      • 2016-10-26
      • 1970-01-01
      相关资源
      最近更新 更多