问题
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.
回答1:
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>
来源:https://stackoverflow.com/questions/54167970/disabled-controls-from-formgroup-part-of-form-custom-form-control-are-excluded