There is an example form Angular University how to use nested forms with ControlValueAccessor.
They create a separate form component and use it as a sub-form:
<div [formGroup]="form">
... other form controls
<address-form formControlName="address" legend="Address"></address-form>
</div>
I want to have all the fields of the nested form marked as touched if I call the markAllAsTouched method on the parent form.
<form [formGroup]="form">
<app-address-form formControlName="address"></app-address-form>
</form>
<button (click)="markAsTouched()">Mark as touched</button>
export class AppComponent {
public form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = fb.group({ address: fb.control({ city: '' }) });
}
public markAsTouched() {
this.form.markAllAsTouched();
}
}
Is it possible using this approach?
I have built a simplified version on StackBlitz.
What actually happens when you click the button is marking the app-address-form as touched:
<app-address-form formcontrolname="address" >
...
</app-address-form>
But I want to propogate it to the child form and its fields.
CodePudding user response:
Really the mannage of nested form groups that explain the link is a bit confused because you really not have nested form groups. You has a FormGroup with one control (the value of the control is an object but you only has one control)
See that in your example you write:
this.form = fb.group({ address: fb.control({ city: '' }) });
A nested formGroup should be like:
this.form = fb.group({ address: fb.group({ city: '' }) });
But this formGroup can not be mannage in the way Angular University say. The way is simply use a component and pass the formGroup as Input(*)
It's the reason because only is touched the "control"
SOLUTION:You can use a template reference variable and pass to your function
<form [formGroup]="form">
<app-address-form #address formControlName="address"></app-address-form>
</form>
<button (click)="markAsTouched(address)">Mark as touched</button>
public markAsTouched(address:any) {
this.form.markAllAsTouched();
//see that you can access to the form of the "adress" simply
//using adress.form
address.form.markAllAsTouched()
}
(*) the component (that not implements from ControlValueAccesor) is simply
@Component({
selector: 'app-address-form',
template: `
<div [formGroup]="form">
<label>City</label>
<input formControlName="city" (blur)="onTouched()">
</div>
`
})
export class AddressFormComponent {
form:FormGroup
@Input('form') set _(value){
this.form=value as FormGroup
}
}
And you use as
<app-address-form [form]=form.get('address')><app-address-form>
CodePudding user response:
Probably there is a better way but, you could achieve this by using an Input
Parent component ts
export class AppComponent {
public form: FormGroup;
setAsTouched: boolean = false;
constructor(private fb: FormBuilder) {
this.form = fb.group({ address: fb.control({ city: '' }) });
}
public markAsTouched() {
this.setAsTouched = true;
}
}
Parent template component
<h1>Form</h1>
<form [formGroup]="form">
<app-address-form
formControlName="address"
[mark]="setAsTouched"
></app-address-form>
</form>
<button (click)="markAsTouched()">Mark as touched</button>
Child component ts
...
...
export class AddressFormComponent implements ControlValueAccessor, Validator {
@Input() set mark(isTouched: boolean) {
if (isTouched) {
this.form.markAllAsTouched();
}
}
...
...
...
}