本文介绍了来自 FormGroup 的禁用控件(表单自定义表单控件的一部分)被父级中的 .getRawValue() 排除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

 getRawValue(): 任何 {返回 this._reduceChildren({}, (acc: {[k: string]: AbstractControl}, control: AbstractControl, name: string) =>{acc[name] = FormControl 的控件实例?control.value : (<any>control).getRawValue();返回acc;});}

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

Stackblitz 演示

重现步骤:

1) 在 UI 中显示的三个自定义表单控件中的任何一个上单击禁用年份"按钮.

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

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

解决方案

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

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

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

export class DetailsFields 实现 ControlValueAccessor,OnInit,OnDestroy {...onChange: (v:any) =>void = () =>{};//<--定义一个函数isAlive:boolean=true;//<--用于取消订阅,见下文registerOnChange(fn: (v: any) => void) {this.onChange = fn;//<--等于函数}//在ngOnInit中ngOnInit(){this.formGroup.valueChanges.pipe(takeWhile(()=>this.isAlive)).subscribe(v=>{//返回 this.formGroup.getRawValue()this.onChange(this.formGroup.getRawValue())})}//在 ngOnDestroyngOnDestroy() {//使 isAlive=False 取消订阅this.isAlive=false;}

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

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

<div formArrayName="汽车"><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>

看到在一个 formArray 中,我们遍历 form.get('cars').controls 并且我们需要放置一个 [formGroupName]="i".在组件中只需作为输入 [formGroup] form.get('cars').at(i)

当然,您需要更改函数createCars"以返回一个 formGroup.不是返回对象类型 {make:..,color:..,year}

的 formControl

createCar(car: any) {//返回一个formGroup,而不是一个formControl返回 this.builder.group({制造:汽车制造,颜色:汽车颜色,年份:车.年});}

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

details-fields.component.ts

@Component({选择器:'app-details-fields',templateUrl: './details-fields.component.html',styleUrls: ['./details-fields.component.css'] ,})导出类 DetailsFields {@Input() 表单组:表单组禁用年(){this.formGroup.get('year').disable();}启用年(){this.formGroup.get('year').enable();}}

details-fields.component.html

<div class="car-wrap"><div><p class="title">这辆车是{{formGroup.get('make').value}}</p><div><input type="text" formControlName="make"><input type="number" formControlName="year"><input type="text" formControlName="color">

<div><button style="margin-top: 3px;"(click)="enableYear()">启用年份</button><button style="margin-top: 3px;"(click)="disableYear()">禁用年份</button>

Having a component that implements ControlValueAccessor, with internal FormGroup to maintain the state of custom form control. When any field, that's part of that FormGroup is disabled, the field isn't visible when calling .getRawValue() method in parent form .

By specification, .getRawValue() should return the raw object, including disabled fields.

I've checked the code of .getRawValue() and here's what I found:

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

So, basically, when form control is instance of FormControl (that's the case when using custom form controls, correct?), it retrieves .value instead of .getRawValue() and that's why the disabled controls of nested form are not included in the final object.

Stackblitz demo

Steps to reproduce:

1) Click on "Disable year" button on any of three custom form controls displayed in the UI.

2) Examine the output below => .getRawValue() and .value responses are identical.

Do you have any idea how I can overcome this? I'm looking for a way to retrieve the disabled controls as well in the parent form.

解决方案

Kav, in your custom form control you has

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

so, your component return the "value" of formGroup. As a control is disabled, the value not return this field. You can change your customControl to return the rawValue of the formGroup, for this you need create a onChangeFunction, and in a ngOnInit subscribe to changes and send the rawValues. As we subscribe it's good unsubscribe using a takeWhile and a variable

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;
    }

But in this case, you received the year always is enabled or not

There're another aproach, that is not have a custom form control, just a component to manage the make,year and color. For this, the first is change your app-component and create the form like another form with a 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>

See that in a formArray we iterate over form.get('cars').controls and we need put a [formGroupName]="i". In the component simply pass as input [formGroup] form.get('cars').at(i)

Of course, you need change your function "createCars" to return a formGroup. not a formControl that return an object type {make:..,color:..,year}

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

Well, the details-fields becomes easer:

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>

这篇关于来自 FormGroup 的禁用控件(表单自定义表单控件的一部分)被父级中的 .getRawValue() 排除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 02:20