在 Angular 中使用 ControlValueAccessor 继承验证

Posted

技术标签:

【中文标题】在 Angular 中使用 ControlValueAccessor 继承验证【英文标题】:Inheriting validation using ControlValueAccessor in Angular 【发布时间】:2018-01-15 08:18:07 【问题描述】:

我有一个自定义表单控件组件(它是一个美化的输入)。它是一个自定义组件的原因是为了便于 UI 更改 - 即,如果我们从根本上更改输入控件样式的方式,那么将很容易在整个应用程序中传播更改。

目前我们在 Angular 中使用 Material Design https://material.angular.io

哪些样式在无效时可以很好地控制。

我们已经实现了 ControlValueAccessor 以允许我们将一个 formControlName 传递给我们的自定义组件,它可以完美地工作;当自定义控件有效/无效并且应用程序按预期运行时,表单有效/无效。

但是,问题是我们需要根据自定义组件内的 UI 是否无效来设置 UI 样式,而我们似乎无法做到 - 实际需要设置样式的输入永远不会经过验证,它只是将数据传入和传出父组件。

组件.ts

import  Component, forwardRef, Input, OnInit  from '@angular/core';
import 
    AbstractControl,
    ControlValueAccessor,
    NG_VALIDATORS,
    NG_VALUE_ACCESSOR,
    ValidationErrors,
    Validator,
 from '@angular/forms';

@Component(
  selector: 'app-input',
  templateUrl: './input.component.html',
  styleUrls: ['./input.component.css'],
  providers: [
    
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputComponent),
      multi: true
    
  ]
)
export class InputComponent implements OnInit, ControlValueAccessor 
  writeValue(obj: any): void 
    this._value = obj;
  
  registerOnChange(fn: any): void 
    this.onChanged = fn;
  
  registerOnTouched(fn: any): void 
    this.onTouched = fn;
  
  setDisabledState?(isDisabled: boolean): void 
    this.disabled = isDisabled;
  

  get value() 
    return this._value;
  

  set value(value: any) 
    if (this._value !== value) 
      this._value = value;
      this.onChanged(value);
    
  

  @Input() type: string;

  onBlur() 
    this.onTouched();
  

  private onTouched = () => ;
  private onChanged = (_: any) => ;
  disabled: boolean;

  private _value: any;

  constructor()  

  ngOnInit() 
  


组件.html

<ng-container [ngSwitch]="type">
  <md-input-container class="full-width" *ngSwitchCase="'text'">
    <span mdPrefix><md-icon>lock_outline</md-icon> &nbsp; </span>
    <input mdInput placeholder="Password" type="text" [(ngModel)]="value" (blur)="onBlur()" />
  </md-input-container>
</ng-container>

页面使用示例:

HTML:

<app-input type="text" formControlName="foo"></app-input>

TS:

this.form = this.fb.group(
        foo: [null, Validators.required]
    );

【问题讨论】:

也许这就是您一直在寻找的? ***.com/questions/48573931/… 【参考方案1】:

您可以通过 DI 访问NgControlNgControl 包含有关验证状态的所有信息。要检索NgControl,您不应通过NG_VALUE_ACCESSOR 提供组件,而应在构造函数中设置访问器。

@Component(
  selector: 'custom-form-comp',
  templateUrl: '..',
  styleUrls: ...
)
export class CustomComponent implements ControlValueAccessor 

   constructor(@Self() @Optional() private control: NgControl) 
     this.control.valueAccessor = this;
   

   // ControlValueAccessor methods and others

   public get invalid(): boolean 
     return this.control ? this.control.invalid : false;
   

   public get showError(): boolean 
      if (!this.control) 
       return false;
      

      const  dirty, touched  = this.control;

      return this.invalid ? (dirty || touched) : false;
   

请通过article 了解完整信息。

【讨论】:

所以我们通过注入的 NgControl 检索到 formControlName="foo" 是否有效的事实,对吗?我们可以在 custom-form-comp 的模板中使用 get invalid() 布尔变量来设置其内部元素的样式,对吗?有没有办法(并且有意义)将类 ng-touched、ng-dirty、ng-invalid 等从 组件转发到其内部元素? 对于 Angular 8,我可以将构造函数简化为 constructor(private control: NgControl) this.control.valueAccessor = this; 嗨@VJAI,我正在尝试接受您的答案,并将其应用于这个问题,这有点不同,您有更优化的答案吗? ***.com/questions/59086347/… 谢谢 这很有帮助,尽管我不得不重新阅读几次才注意到我需要停止通过NG_VALUE_ACCESSOR 提供并改用.valueAccessor。谢谢! @Coderer 感谢您的评论,我能够使它工作,我得到一个循环依赖错误..在答案中没有注意到提供者数组已被省略..很好! 【参考方案2】:

另外:可能被认为是肮脏的,但它对我有用:

    让您的组件实现Validator 接口。 2 在验证函数中,您使用 controlcontainer 来获取组件的外部表单控件。 使用变量跟踪父表单控件的状态(VALID/INVALID)。 检查触摸。并仅在触摸为真且状态已更改时对您的字段执行验证操作。

【讨论】:

你能提供一个例子——尤其是validate方法吗? 这个答案需要更多细节。考虑发布一个堆栈闪电战【参考方案3】:

在这里找到答案:

Get access to FormControl from the custom form component in Angular

不确定这是不是最好的方法,我希望有人找到更漂亮的方法,但是将子输入绑定到以这种方式获得的表单控件解决了我们的问题

【讨论】:

我遇到了完全相同的问题。获取控制器后,您是如何将验证反映到输入的? 很好地指出了正确的方向,但事实上,你是怎么做到的? 感谢您将我引向正确的方向,该页面上真正为我工作的答案是this one

以上是关于在 Angular 中使用 ControlValueAccessor 继承验证的主要内容,如果未能解决你的问题,请参考以下文章

在 Storybook 中使用 @angular-redux/store 测试 Angular

Angular - 在服务和组件中使用管道

在 Angular 5 和 AOT-Build 中使用 @angular 编译器时出错

在Angular 2+项目中使用AngularJS Material组件

在 Nativescript 代码共享项目中使用 Angular 库

如何通过 Angular 方式示例在 Angular DataTables 中使用服务器端选项?