【问题标题】:Disabled controls from FormGroup (part of form custom form control) are excluded by .getRawValue() in parent来自 FormGroup 的禁用控件(表单自定义表单控件的一部分)被父级中的 .getRawValue() 排除
【发布时间】:2019-06-07 15:24:15
【问题描述】:

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

按照规范,.getRawValue() 应该返回原始对象,包括禁用的字段。

我检查了.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 中显示的三个自定义表单控件中的任意一个上单击“禁用年份”按钮。

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

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

【问题讨论】:

    标签: angular angular2-forms angular-reactive-forms angular-forms


    【解决方案1】:

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

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

    所以,您的组件返回 formGroup 的“值”。由于某个控件被禁用,该字段的值不会返回。您可以更改您的 customControl 以返回 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”来返回一个formGroup。不是返回对象类型 {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
        });
      }
    

    嗯,详细信息字段变得更容易:

    details-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();
        }
    }
    

    details-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>
    

    【讨论】:

    • 你摇滚!我决定采用第一种方法,因为我创建了一个非常简单的演示来说明问题所在。实际上,我的自定义控件有更多的职责,等等。谢谢!
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 2018-10-15
      • 2017-03-02
      • 1970-01-01
      • 2022-07-29
      • 2017-07-02
      • 2020-02-03
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多