来自 FormGroup 的禁用控件(表单自定义表单控件的一部分)被父级中的 .getRawValue() 排除

Disabled controls from FormGroup (part of form custom form control) are excluded by .getRawValue() in parent

有一个实现 ControlValueAccessor 的组件,内部 FormGroup 用于维护自定义表单控件的状态。当任何字段,即 FormGroup 的一部分被禁用时,该字段在父表单中调用 .getRawValue() 方法时不可见。

根据规范,.getRawValue() 应该 return 原始对象,包括禁用的字段。

我检查了 .getRawValue() 的代码,这是我发现的:

 getRawValue(): any {
    return this._reduceChildren(
        {}, (acc: {[k: string]: AbstractControl}, control: AbstractControl, name: string) => {
          acc[name] = control instanceof FormControl ? control.value : (<any>control).getRawValue();
          return acc;
        });
  }

所以,基本上,当表单控件是 FormControl 的实例时(使用自定义表单控件时就是这种情况,对吗?),它检索 .value 而不是 .getRawValue(),这就是为什么嵌套表单的禁用控件未包含在最终对象中。

Stackblitz demo

重现步骤:

1) 单击 UI 中显示的三个自定义表单控件中任意一个的 "Disable year" 按钮。

2) 检查下面的输出 => .getRawValue().value 响应相同。

你知道我该如何克服这个问题吗?我正在寻找一种方法来检索父窗体中禁用的控件。

Kav,在您的自定义表单控件中您有

  registerOnChange(fn: (v: any) => void) {
        this.formGroup.valueChanges.subscribe(fn);
    }

因此,您的组件 return 是 formGroup 的 "value"。由于某个控件被禁用,因此该字段的值不是return。您可以将 customControl 更改为 return formGroup 的 rawValue,为此您需要创建一个 onChangeFunction,并在 ngOnInit 中订阅更改并发送 rawValues。当我们订阅时,最好使用 takeWhile 和变量取消订阅

export class DetailsFields implements ControlValueAccessor,OnInit,OnDestroy {
    ...
    onChange: (v:any) => void = () => {}; //<--define a function
    isAlive:boolean=true; //<--use to unsubscribe, see below

    registerOnChange(fn: (v: any) => void) {
      this.onChange = fn; //<--equal to function
    }

    //In ngOnInit
    ngOnInit()
    {
      this.formGroup.valueChanges.pipe(takeWhile(()=>this.isAlive))
        .subscribe(v=>{
        //return this.formGroup.getRawValue()
        this.onChange(this.formGroup.getRawValue())
      })
    }
    //In ngOnDestroy
    ngOnDestroy() {  //make isAlive=False to unsubscribe
      this.isAlive=false;
    }

但在这种情况下,您收到的年份总是启用或未启用

还有另一种方法,没有自定义表单控件,只有一个组件来管理品牌、年份和颜色。为此,首先是更改您的应用程序组件并像使用 formArray 的另一个表单一样创建表单。

<div id="cars" [formGroup]="form">
  <div formArrayName="cars">
  <div *ngFor="let car of form.get('cars').controls; let i = index;" 
   [formGroupName]="i">
    <app-details-fields [formGroup]="form.get('cars').at(i)" ></app-details-fields>
  </div>
  </div>
</div>

看到在 formArray 中我们迭代了 form.get('cars').controls 并且我们需要放置一个 [formGroupName]="i"。在组件中简单地作为输入传递 [formGroup] form.get('cars').at(i)

当然,您需要将函数 "createCars" 更改为 return formGroup。不是 return 对象类型 {make:..,color:..,year}

的 formControl
createCar(car: any) {  //return a formGroup,not a formControl
    return this.builder.group({
      make: car.make,
      color: car.color,
      year: car.year
    });
  }

好吧,详细信息字段变得更容易了:

详情-fields.component.ts

@Component({
  selector: 'app-details-fields',
  templateUrl: './details-fields.component.html',
  styleUrls: ['./details-fields.component.css']  ,
})

export class DetailsFields {
   @Input() formGroup:FormGroup

   disableYear() {
      this.formGroup.get('year').disable();
    }

    enableYear() {
      this.formGroup.get('year').enable();
    }
}

详情-fields.component.html

<div [formGroup]="formGroup">
  <div class="car-wrap">
      <div>
          <p class="title">This car is a {{formGroup.get('make').value}}</p>
      <div>
        <input type="text" formControlName="make">
        <input type="number" formControlName="year">
        <input type="text" formControlName="color">
      </div>
      <div>
        <button style="margin-top: 3px;" (click)="enableYear()">Enable year</button>
        <button style="margin-top: 3px;" (click)="disableYear()">Disable year</button>
      </div>
    </div>
  </div>
</div>

或者您可以简单地这样做:

registerOnChange(fn: (v: any) => void) {
    this.formGroup.valueChanges.pipe(map(_ => this.formGroup.getRawValue())).subscribe(fn);
}