Issue
I am working on a form as below
My code
nested-form.component.ts
public nestedForm: FormGroup;
constructor(
private formBuilder: FormBuilder) {
this.calibrationForm = this.buildFormGroup(formBuilder);
}
buildFormGroup(formBuilder: FormBuilder): FormGroup {
return new FormGroup({
motor: new FormControl('', Validators.required),
properties: new FormControl(""),
});
}
save() {
console.log(this.nestedForm.value);
}
nested-form.component.html
<form [formGroup]="nestedForm" >
<mat-select class="field-select" [(ngModel)]="_selectedMotor"
(selectionChange)="selectMotor($event.value)" formControlName="motor">
<mat-option class="select-option" *ngFor="let item of motors" [value]="item">
item</b>
</mat-option>
</mat-select>
<app-properties formControlName="properties" [type] = "_selectedMotor"></app-properties>
...
</form>
properties.component.ts
@Component({
...
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PropertiesComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => PropertiesComponent),
multi: true
}
]
})
export class PropertiesComponent implements OnInit, ControlValueAccessor {
@Input() type: string = null;
public propertiesForm: FormGroup;
ngOnInit(): void {
this.propertiesForm = this.buildFormGroup(this.formBuilder);
}
ngOnChanges(changes): void {
this.propertiesForm = this.buildFormGroup(this.formBuilder);
}
buildFormGroup(formBuilder: FormBuilder): FormGroup {
switch (this.type) {
case "OLD":
return new FormGroup(
{
tilt: new FormControl(0, [Validators.required]),
fine_tilt: new FormControl(0, [Validators.required]),
rotate: new FormControl(0, [Validators.required]),
fine_rotate: new FormControl(0, [Validators.required])
});
case "VINTEN":
return new FormGroup(
{
vinten_tilt: new FormControl(0, [Validators.required]),
vinten_rotate: new FormControl(0, [Validators.required])
});
}
}
public onTouched: () => void = () => { };
writeValue(val: any): void {
val && this.propertiesForm.setValue(val, { emitEvent: false });
}
registerOnChange(fn: any): void {
this.propertiesForm.valueChanges.subscribe(fn);
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
isDisabled ? this.propertiesForm.disable() : this.propertiesForm.enable();
}
validate(c: AbstractControl): ValidationErrors | null {
return this.propertiesForm.valid ? null : { invalidForm: { valid: false, message: "propertiesForm fields are invalid" } };
}
...
}
properties.component.html
<form *ngIf="type" class="col" [formGroup]="propertiesForm" [ngSwitch]="type">
<div *ngSwitchCase="OLD">
<input type="number" class="field-input" id="rotate" formControlName="rotate" />
<input type="number" class="field-input" id="fine_rotate" formControlName="fine_rotate" />
<input type="number" class="field-input" id="tilt" formControlName="tilt" />
<input type="number" class="field-input" id="fine_tilt" formControlName="fine_tilt" />
</div>
<div *ngSwitchCase="VINTEN">
<input type="number" class="field-input" id="rotate" formControlName="vinten_rotate" />
<input type="number" class="field-input" id="tilt" formControlName="vinten_tilt" />
</div>
</form>
Result:
Let’s say OLD is a default, when the form is loaded.
I entered all fields and click Save button for OLD, the form values is collected correctly as expected.
However, when I switched from OLD to VINTEN, the form UI is updated but valid/invalid state of Save button is not working any more, and after clicking Save button, the form values are still the OLD properties values, not VINTEN properties values.
Did I miss something or make something wrong? Any suggestion is appreciated.
Solution
To notify parent control about internal changes you have to listen propertiesForm.valueChanges()
.
And call fn
that you were given by registerOnChange(fn: any): void
onModelChange = () => {}
ngOnInit(): void {
this.propertiesForm = this.buildFormGroup(this.formBuilder);
this.propertiesForm.valueChanges().subscribe(value => this.onModelChange(value))
}
registerOnChange(fn: any): void {
this.onModelChange = fn;
}