以模板驱动的形式自定义ControlValueAccessor

时间:2017-09-30 10:13:59

标签: javascript angular angular2-template angular2-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属性(也称为无输入字段)设置为true。

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

有什么想法吗?

1 个答案:

答案 0 :(得分:0)

嗯,这一点并不明显,直到我找到了将值设置为this.messaging.getToken() .then(oldToken => { this.messaging.deleteToken(oldToken).then(() => { this.messaging.getToken().then(newToken => { this.updateToken(newToken) }) }) }) 的方式,事实证明,使用变量名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也是一个可以在别处检查的属性,也是一个TypeScript接口 - 而且,tada,它可以工作。 / p>