【问题标题】:How to propagate validators and CSS styles to input control inside a custom Angular form component?如何将验证器和 CSS 样式传播到自定义 Angular 表单组件中的输入控件?
【发布时间】:2020-01-31 03:42:01
【问题描述】:

我有一个嵌套表单组件,其中有一个 <input> 字段设置为与 formControlName="nested" 一起使用。验证器在父 FormControl 上设置,如下所示:

  form = this.fb.group({
    value: ['', [Validators.required, Validators.minLength(3)]],
    nested: ['', [Validators.required, Validators.minLength(3)]],
  });

我想将状态从父 FormControl 传播到嵌套 <input>,因此它的反应方式与常规的非嵌套 <input> 相同,它并单击提交(control.markAsTouched()),状态设置为无效,CSS 样式ng-invalid 已设置。

我设法获得了 部分胜利 after reading this SO post 使用以下代码订阅父控件的状态更改,但“触摸”嵌套输入会将其恢复为 VALID 并单击提交将没有设置ng-invalid 样式。

@ViewChild('tbNgModel', {static: false}) tbNgModel: NgModel;

private isAlive = true;

constructor(@Optional() @Self() public ngControl: NgControl, private cdr: ChangeDetectorRef) {
    if (this.ngControl != null) {
        this.ngControl.valueAccessor = this;
    }
}

ngAfterViewInit(): void {
    this.ngControl.statusChanges.pipe(
        takeWhile(() => this.isAlive)
    ).subscribe(status => {
        console.log('Status changed: ', status, 'Errors: ', this.ngControl.errors);
        this.tbNgModel.control.setErrors(this.ngControl.errors);
        this.cdr.detectChanges();
    });

    this.ngControl.control.updateValueAndValidity(); // to force emit initial value
}

Stackblitz reproduction of my problem

我如何才能真正将状态传播到嵌套控件,同时仅在父级 FormControl 上设置验证器?

最终解决方案

来自@Eliseo 的回答和this other SO post,这是我最终完成的实现(它对我有用,但可能还有更好的方法)

component.ts

    constructor(@Optional() @Self() public ngControl: NgControl) {
        if (this.ngControl != null) {
            this.ngControl.valueAccessor = this;
        }
    }

...

    getValidationCss() {
        if (!this.ngControl) return {};

        return {
            'ng-invalid': this.ngControl.invalid,
            'is-invalid': this.ngControl.invalid && this.ngControl.touched,
            'ng-valid': this.ngControl.valid,
            'ng-touched': this.ngControl.touched,
            'ng-untouched': this.ngControl.untouched,
            'ng-pristine': this.ngControl.pristine,
            'ng-dirty': this.ngControl.dirty,
        };
    }

component.html

...
<input #tb class="form-control" [ngClass]="getValidationCss()" ...>
...

【问题讨论】:

    标签: angular angular7 angular8


    【解决方案1】:

    Dstj,事情一定更简单。见stackblitz

    在我们的输入中我们可以使用 [ngClass]

    <input [ngClass]="{'ng-touched':ngControl.control.touched,'ng-invalid':ngControl.control.invalid}" 
    type="text" class="form-control" [(ngModel)]="value" 
           (ngModelChange)="propagateChange($event)"
           (blur)="touched()"
     > 
    

    其中ngControl是构造函数中的ngControl注入

    constructor(@Self() public ngControl: NgControl) {
            if (this.ngControl != null) {
                this.ngControl.valueAccessor = this;
            }
        }
    

    看看那是ngControl谁是有效/无效触摸/未触摸....

    更新添加(模糊)以标记为已触摸

    更新2使用 ngDoCheck

    其他解决方案是使用ngDoCheck,

    我们的组件

      value: string;
      customClass=null;
      onChange:boolean=false;
    
        constructor(@Self() public ngControl: NgControl,private el:ElementRef) {
            if (this.ngControl != null) {
                this.ngControl.valueAccessor = this;
            }
        }
      ngDoCheck()
      {
        if (!this.onChange)
        {
          this.onChange=true;
          //it's necesary a setTimeOut to give Angular a chance
          setTimeout(()=>{
            this.customClass=this.el.nativeElement.getAttribute('class');
            this.onChange=false;
          })
        }
      }
      change(value:any)
      {
        this.propagateChange(value);
        this.touched(null)
      }
    

    .html

    <input #tbNgModel="ngModel" [ngClass]="customClass" type="text" class="form-control" 
         [(ngModel)]="value" 
         (ngModelChange)="change($event)" 
         (blur)="touched()"> 
    

    【讨论】:

    • 谢谢,但如果您只是“循环”通过(触摸而不更改任何内容),则您的示例不适用于 ng-invalid。似乎它在某处缺少“刷新”,因为它设置为ng-valid,即使ngControl.control.invalid=true
    • ::glups:: 我忘了指明何时被触摸,你可以使用 (blur)="touched()"。刚刚在 stackblitz 和答案中更正。是的,控件无效,但输入有效
    • 谢谢。我仍然觉得必须重新编码 ng-* CSS 类的应用程序,(blur) 事件很奇怪,但它确实有效...... ;)
    • 在进一步的测试中,它只是半有效的,因为输入的底层 ngModel 中的状态仍然有效,所以我得到了 class="ng-valid ng-invalid" 两者。我需要将状态设为无效,因为在我的真实应用程序中,我还有一个自定义指令,用于查找 control.invalid 状态以应用 boostrap 的 is-invalid CSS 类。如果底层 ngModel 保持有效,则它不起作用。见stackblitz.com/edit/angular-d28achf-xawuef
    • 思考问题后决定使用DoCheck将类属性复制到输入中,看我更新的答案和stackblitzstackblitz.com/edit/…
    猜你喜欢
    • 1970-01-01
    • 2020-06-12
    • 2018-07-04
    • 2016-12-17
    • 1970-01-01
    • 2022-11-24
    • 2020-04-06
    • 1970-01-01
    • 2020-07-12
    相关资源
    最近更新 更多