繁体   English   中英

模板驱动表单中的自定义 ControlValueAccessor

[英]Custom ControlValueAccessor in template-driven forms

我有一个自定义的<data-input-text>组件,它有两种模式:常规和禁用。 这是模板(我为演示案例简化了一点):

<label  *ngIf="!disabled"
    class="field-label" 
    [ngClass]="{'focused' : isFocused, 'with-errors' : errors}">
    <input class="field-value" 
            [type]="type"
            [required]="required"
            (focus)="onFocus()"
            (blur)="onBlur()"
            [(ngModel)]="value"
            #fieldInput="ngModel">

</label>
<div class="field-label" *ngIf="disabled">
    <span class="field-value">{{ value }}</span>
    <span class="field-name">{{ label }}</span>
</div>

父窗体中,我按以下方式使用此组件:

<form #profileForm="ngForm">
    <data-text-input
          label="lastName"
          [required]="true"
          [disabled]="userIsRegistered"                    
          name="lastName"
          ngModel></data-text-input>
</form>

userIsRegistered返回一个布尔值,它应该在组件内的输入字段或跨度之间切换。 直到这里一切正常。

在父组件中设置表单以匹配 BehaviorSubject,如下所示:

 this._sub = this.dl.selectedEmployee.subscribe( u => {
  if ( u.id ) {
    this.isLoading = false;
      setTimeout( () => {
        this.profileForm.setValue(u);
        this.profileForm.control.markAsPristine();
      }, 10);
  }
});

这是自定义 ControlValueAccessor组件

import { Component, Input, ViewChild, forwardRef, 
      AfterViewInit, OnInit, OnChanges, 
      NgModule } from '@angular/core';

import { NG_VALUE_ACCESSOR, NG_VALIDATORS, 
      ControlValueAccessor, FormControl, 
      Validator, NgForm } from '@angular/forms';

 @Component({
   selector: 'data-text-input',
   template: `
   <label  *ngIf="!disabled"
       class="field-label">
       <input class="field-value" 
               [type]="type"
               [required]="required"
               (blur)="onBlur()"
               [(ngModel)]="value"
               #fieldValue="ngModel">
       <span class="field-name">{{ label }}</span>

   </label>
   <div class="field-label" *ngIf="disabled">
       <span class="field-value">{{ value }}</span>
       <span class="field-name">{{ label }}</span>
   </div>
     `,
     providers: [
       {
         provide: NG_VALUE_ACCESSOR,
         useExisting: forwardRef( ()=> DataTextInputComponent ),
         multi: true
       },
       {
         provide: NG_VALIDATORS,
         useExisting: forwardRef( ()=> DataTextInputComponent ),
         multi: true
       }
     ]
   })

   export class DataTextInputComponent implements OnChanges, ControlValueAccessor, Validator  {

@Input() public disabled: boolean = false;
@Input() public label: string;
@Input() public required: boolean = false;
@Input() public type: string = 'text';
@ViewChild('fieldValue') public fieldValue: FormControl;

// infrastructure
public registerOnChange(fn: any) { this.propagateChange = fn; }
public registerOnTouched(fn: any) { this.propagateTouch = fn; }

private propagateChange = (_: any) => { };
private propagateTouch = (_: any) => { };

/**
 * inner value
 */
private innerValue: any = null;

/**
 * on changes hook
 */
public ngOnChanges(): void {
    if ( this.disabled ) {
        this.propagateChange(this.innerValue);
    }
}

/**
 * input events
 */
public onBlur(): void {
    this.propagateChange(this.innerValue);
    this.propagateTouch(true);
}

/**
 * value accessor setter and getter
 */
public get value(): any {
    return this.innerValue;
};

public set value(value: any) {
    if ( value !== 'undefined' ) {
        this.innerValue = value;
        this.propagateChange(value);
        this.propagateTouch(true);
    }
}

/**
 * value accessor implementation
 * @param value 
 */
public writeValue(value: any): void {
    if (value !== this.innerValue) {
        this.innerValue = value;
    }
}

/**
 * validation
 * @param c 
 */
public validate(c: FormControl) {
    return this.errors = (this.disabled) ? null : this.customValidate(c);
}
private customValidate(c: FormControl): {} {
  if ( c.touched ) {
    // some validation logic which is not relevant here;
    return null;
  }
  return null;
}
}

表单中还使用了其他组件(如颜色选择器和 ng-select)。

所以奇怪的部分是这个。 表单值设置好了。 没有错误。 这些值在数据文本输入组件以及表单中的其他组件中正确显示(对于disabled!disabled )。 奇怪的是,当我用调试器检查this.profileForm对象时, controls属性具有所有具有各自值的控件,但表单的value属性错过了那些,其中disabled属性(又名无输入字段)是设置为真。

这是 Plunker: https ://plnkr.co/edit/nbWQZzQjhGae622CanGa ? p = preview

有任何想法吗?

嗯,这并不明显,直到我追踪到AbstractControl.prototype.updateValueAndValidity设置值的方式,结果证明,使用disabled变量名在这里是一个坏主意:

<form #profileForm="ngForm">
    <data-text-input
          label="lastName"
          [required]="true"
          [disabled]="userIsRegistered"                    
          name="lastName"
          ngModel></data-text-input>
</form>

我已将disabled属性重命名为isReadOnly因为readonly也是一个可以在其他地方检查的属性,也是一个 TypeScript 接口——而且,tada,它可以工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM