Angular reactive forms - manually trigger validation of parent FormGroup when child FormGroup changes

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?

1 answer

  • answered 2018-05-16 05:55 fateme fazli

    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>