Angular Material自定义窗体字段控件的值不会在调用FormGroup中更新

时间:2019-08-18 10:21:39

标签: angular angular-material2 angular-reactive-forms

我按照this指南在Angular Material中创建了一个自定义表单字段控件。

然后我将此控件添加到FormGroup中。但是我这里的问题是FormGroup无法获得自定义控件的正确值。它总是得到undefined。我确实检查了是否从输入中将正确的值播种到自定义控件中的value属性中。

这里可能是什么问题?

我的自定义控件: 组件

import { Component, OnDestroy, HostBinding, Input, Optional, Self, ElementRef } from '@angular/core';
import { MatFormFieldControl } from '@angular/material/form-field';
import { Subject } from 'rxjs/internal/Subject';
import { NgControl, ControlValueAccessor, FormBuilder, FormGroup } from '@angular/forms';
import { FocusMonitor } from '@angular/cdk/a11y';
import { coerceBooleanProperty } from '@angular/cdk/coercion';

@Component({
  selector: 'app-test-input',
  templateUrl: './test-input.html',
  styleUrls: ['./test-input.scss'],
  providers: [{ provide: MatFormFieldControl, useExisting: MyTestInput }]
})
export class MyTestInput implements MatFormFieldControl<string>, OnDestroy, ControlValueAccessor {
  static nextId = 0;
  FormGrp: FormGroup;
  stateChanges = new Subject<void>();
  private val: string;
  private ph: string;
  private req = false;
  private dis = false;
  onChange: () => void;
  onTouched: () => void;

  public get value(): string {
    return this.val;
  }

  public set value(val: string) {
    this.val = val;
    this.stateChanges.next();
  }

  controlType = 'my-test-input';

  @HostBinding() id = `${this.controlType}-${MyTestInput.nextId++}`;

  @Input()
  get placeholder() {
    return this.ph;
  }
  set placeholder(plh) {
    this.ph = plh;
    this.stateChanges.next();
  }

  focused = false;

  get empty() {
    return false;
  }

  @HostBinding('class.floating')
  get shouldLabelFloat() {
    return this.focused || !this.empty;
  }

  @Input()
  get required() {
    return this.req;
  }
  set required(req) {
    this.req = coerceBooleanProperty(req);
    this.stateChanges.next();
  }

  @Input()
  get disabled(): boolean { return this.dis; }
  set disabled(value: boolean) {
    this.dis = coerceBooleanProperty(value);
    this.stateChanges.next();
  }

  errorState = this.FormGrp == null ? false : this.FormGrp.invalid;

  @HostBinding('attr.aria-describedby') describedBy = '';

  setDescribedByIds(ids: string[]) {
    this.describedBy = ids.join(' ');
  }

  onContainerClick(event: MouseEvent) {
    if ((event.target as Element).tagName.toLowerCase() !== 'input') {
      this.elRef.nativeElement.querySelector('input').focus();
    }
    this.onTouched();
  }

  ngOnDestroy() {
    this.stateChanges.complete();
    this.fm.stopMonitoring(this.elRef.nativeElement);
  }


  constructor(
    @Optional() @Self() public ngControl: NgControl,
    fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) {
    this.FormGrp = fb.group({
      data: ['', this.required]
    });
    if (ngControl != null) {
      ngControl.valueAccessor = this;
    }
    fm.monitor(elRef.nativeElement, true).subscribe(origin => {
      this.focused = !!origin;
      this.stateChanges.next();
    });
  }

  writeValue(value: any): void {
    this.FormGrp.get('data').setValue(value);
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    isDisabled ? this.FormGrp.get('data').disable() : this.FormGrp.get('data').enable();
  }

  input() {
    this.value = this.FormGrp.get('data').value;
    this.onChange();
  }
}

模板:

<div [formGroup]="FormGrp">
    <input formControlName="data" (input)="input()">
</div>

我的电话表格:

<form [formGroup]="Form">  
    <mat-form-field>
        <app-test-input formControlName="testControl"></app-test-input>
    </mat-form-field>
    <button>Submit</button>
</form>
<p *ngIf="Form">
    {{Form.value | json}}
</p>

我的呼叫表单定义:

this.Form = fb.group({
  testControl: ['', [Validators.required]]
});

1 个答案:

答案 0 :(得分:2)

您忘了将更新的值传递给ControlValueAccessor实现的一部分onChange方法:

test-input.component.ts

export class TestInputComponent ... {
 onChange = (_: any) => {};

 ...
 input() {
    this.value = this.FormGrp.get('data').value;

    this.onChange(this.value);
                      \/
                  pass newValue
  }

Stackblitz Example