0

I use nested reactive form as FormControl.

Here is demo https://stackblitz.com/edit/angular-pmfq5z?file=app%2Fapp.component.ts

Issue: the main form is valid even if nested is invalid (try to clear required fields).

How the main form can be notified about nested form validity change?

The main form component:

import { Component, Input } from '@angular/core';
import { FormBuilder, FormGroup, ControlContainer, NgForm, Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  template: `
    <h1>Form</h1>
    <pre>Valid: {{ form.valid }}</pre>
    <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)" novalidate>
      <label>name</label>
      <input formControlName="name">
      <app-address-form formControlName="address"></app-address-form>
      <button>submit</button>
    </form>
    <ng-container *ngIf="submitData">
      <span>submit data</span>
      <pre>{{ submitData }}</pre>
    </ng-container>
  `
})
export class AppComponent  {
  @Input() name: string;
  submitData = '';
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = fb.group({
      name: fb.control('foo bar', Validators.required),
      address: fb.control({
        city: 'baz',
        town: 'qux',
      })
    });
  }

  onSubmit(v: any) {
    this.submitData = JSON.stringify(v, null, 2);
  }
}

The nested form component:

import { Component, forwardRef } from '@angular/core';
import { ControlContainer, ControlValueAccessor, FormGroup, FormControl, NG_VALUE_ACCESSOR, NgControl, NgForm, Validators } from '@angular/forms';

// https://docs.google.com/presentation/d/e/2PACX-1vTS20UdnMGqA3ecrv7ww_7CDKQM8VgdH2tbHl94aXgEsYQ2cyjq62ydU3e3ZF_BaQ64kMyQa0INe2oI/pub?slide=id.g293d7d2b9d_1_1532
@Component({
  selector: 'app-address-form',
  template: `
    <div [formGroup]="form">
      <label>city</label>
      <input formControlName="city" (blur)="onTouched()">
      <label>town</label>
      <input formControlName="town" (blur)="onTouched()">
    </div>
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      multi: true,
      useExisting: forwardRef(() => AddressFormComponent)
    }
  ]
})
export class AddressFormComponent implements ControlValueAccessor {
  form: FormGroup = new FormGroup({
    city: new FormControl('', Validators.required),
    town: new FormControl('', Validators.required)
  });

  onTouched: () => void = () => {};

  writeValue(v: any) {
    this.form.setValue(v, { emitEvent: false });
  }

  registerOnChange(fn: (v: any) => void) {
    this.form.valueChanges.subscribe(fn);
  }

  setDisabledState(disabled: boolean) {
    disabled ? this.form.disable() : this.form.enable();
  }

  registerOnTouched(fn: () => void) {
    this.onTouched = fn;
  }
}
4
  • emit event_emitter from child to parent. you can update the parent form validators using setValidators() Commented Dec 15, 2018 at 10:01
  • could you provide more details, please? Commented Dec 15, 2018 at 10:04
  • Provided answer below. Commented Dec 15, 2018 at 10:10
  • github.com/angular/angular/issues/10530 Commented Dec 15, 2018 at 11:02

2 Answers 2

1

Approach1: (using Event Emitter)

export class AddressFormComponent implements ControlValueAccessor {
   @Output formValidStatus = new EventEmitter<any>();

   this.formValidStatus.emit(true);
}

Parent Component Template:

<app-address-form (formValidStatus)="formValidStatus($event)" formControlName="address"></app-address-form>

Parent Component :

formValidStatus(){
  //update form status
}

Approach2:

Inject ParentComponent inside Child Component constructor using Angular DI. You can call parent methods in child component.

constructor(private appc: AppComponent){} 

this.appc.methodname();
Sign up to request clarification or add additional context in comments.

Comments

0

Change your address control to formGroup and add validation there as well

constructor(private fb: FormBuilder) {
    this.form = fb.group({
      name: fb.control('foo bar', Validators.required),
      address: fb.control({
        city: 'baz',
        town: 'qux',
      })
    });
  }

So it should be:

 constructor(private fb: FormBuilder) {
    this.form = fb.group({
      name: fb.control('foo bar', Validators.required),
      address: fb.group({
        city: ['box',Validators.required],
        town: ['qux',Validators.required],
      })
    });
  }

https://stackblitz.com/edit/save-7ucjtn

4 Comments

The main form won't be aware of dynamically added controls in the nested component.
Not bad so far. But still there's an error. Error: control.registerOnChange is not a function
My issue is the opposite. it is always invalid even though the form controls are all valid.
@chitgoks issue is not clear to me if you can update the details or send a email to me

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.