13

I have an Angular component that defines an FormGroup, which has a nested FormGroup as one of its controls.

The child FormGroup is passed as an @Input parameter to a child component, and there are validators on some of the controls inside the child FormGroup.

For some reason, the valid property of the parent FormGroup only updates once I update values in the child component, then make a random change to one of the inputs for the parent FormGroup (like adding an extra space to an input).

The setup is fairly complex (validators are added or removed from controls on the child FormGroup depending on certain conditions, which is probably why the validation isn't happening automatically).

How can I manually trigger the parent FormGroup to re-validate as soon as anything in the child FormGroup changes? I tried this in the child component:

ngOnInit()

  this.myForm.valueChanges.subscribe(val => {
      this.myForm.updateValueAndValidity({onlySelf: false, emitEvent: true})
  });

This is supposed to trigger a re-validation of the child FormGroup whenever any of its inputs change, and broadcast the event to the parent component, which should trigger a re-validation of the parent FormGroup. I get some kind of stack overflow error though, as this results in an infinite loop.

How can I trigger the parent FormGroup to re-validate automatically?

2 Answers 2

9
this.FormGroup.updateValueAndValidity();

updateValueAndValidity() - this is a default method withing FormGroup

Sign up to request clarification or add additional context in comments.

1 Comment

FYI, this also triggers valueChanges. Which you may not want to trigger. An example would be if you are modifying FormGroup on a valueChange and then want it to validate again. Calling this would create an infiinite loop. You could set emitEvent to false, but then the validation would never get triggered.
0

i did something like this for example it's my child formgroup:

my child formgroup component:

import { Component, Input } from "@angular/core";
import { FormGroup } from "@angular/forms";

@Component({
    selector: 'input-form-control',
    templateUrl: './input-form-control.component.html'
})
export class InputFormControlComponent {

    public formGroup: FormGroup;
    @Input('label') formControlLabel: string;
    @Input('controlId') formControlId: string;
    @Input('name') formControlName: string;
    @Input('type') formControlType: string;
    @Input('errorText') formControlErrorText: string;
    @Input('helpText') formControlHelpText: string;

    @Input('group')
    set formControl(group) {
        this.formGroup = group;
    }
    @Input('required') isRequired: boolean;
    @Input('value')
    set value(value: string) {
        this.currentValue = value;
        this.store({ value });
        this.setAsTouched();
    }
}
    get value(): string {
        return this.currentValue;
}
    @Output('valueChange') valueChange: EventEmitter<any> = new EventEmitter();

    setAsTouched() {
    this.formGroup.controls[this.formControlName].markAsTouched();
}
    store(data) {

    if (!this.formGroup.controls[this.formControlName]) {
        return;
    }
    if (data.value === EMPTY_ITEM) {
        this.formGroup.controls[this.formControlName].setValue('');
        this.valueChange.emit({ value: '' });
        return;
    }
    this.formGroup.controls[this.formControlName].setValue(data.value);
    this.valueChange.emit(data);

}
}

it's template:

<form [formGroup]="formGroup" class="m-form m-form--fit">
    <div class="form-group m-form__group row" [ngClass]="{
                    'has-danger': formGroup.controls[formControlName].invalid && formGroup.controls[formControlName].touched,
                    'has-success': formGroup.controls[formControlName].valid && formGroup.controls[formControlName].touched,
                    'has-no-action': formGroup.controls[formControlName].untouched
                                }">
        <label class="col-form-label col-lg-3 col-sm-12" [for]="formControlId">
            {{formControlLabel}}
            <span *ngIf="isRequired" class="required" aria-required="true"> * </span>

        </label>
        <div class="col-lg-4 col-md-9 col-sm-12">
            <input [type]="formControlType || 'text'" class="form-control m-input" [formControlName]="formControlName" [name]="formControlName" [id]="formControlId" [placeholder]="formControlLabel" (click)="setAsTouched()" (valueChanged)="store($event)">
            <div class="form-control-feedback">{{formControlErrorText || 'Required Field May Not Be Empty'}}</div>
            <span class="m-form__help">{{formControlHelpText}}</span>
        </div>
    </div>
    <div class="form-group m-form__group"></div>
</form>

in parent template:

<form [formGroup]="parentFormGroup">
    <input-form-control
        [required]="false"
        [group]="parentFormGroup"
        label="Description"
        name="description"
        controlId="description"
        helpText="Enter the Description"
        [value]="someValue"                    
        (valueChange)="checkParentValidity($event)">
    </input-form-control>
<form>

2 Comments

Thanks @Fateme Fazli, this may be a useful approach in some cases. I discovered that in my case, my child FormGroup was being dynamically replaced at one stage, which was breaking the connection between the parent and child component. If I do a 'patchValue' for each of the controls in the child FormGroup instead, everything works automatically.
@ChrisHalcrow your welcome, would be good and helpful if you post your answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.