angularformarrayvaluechangelistener

Angular: formArray add item and form valueChange


I have a Form which uses a formArray :

  initForm() {
    this.mainForm = this.formBuilder.group({
      foos: this.formBuilder.array([], [Validators.required]),
    });
    
  getFoos(): FormArray {
    return this.mainForm.get('foos') as FormArray;
  }

  onAddFoo() {
    this.getFoos().push(this.formBuilder.control(''));
  }

the Foo item is a subForm :

export interface FooFormValues {
    id: number,
    toto: number,
}

@Component({
  selector: 'ngx-Foo',
  templateUrl: './Foo.component.html',
  styleUrls: ['./Foo.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => FooComponent),
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => FooComponent),
      multi: true
    }
  ]
})
export class FooComponent implements OnInit, OnDestroy, ControlValueAccessor {

  @Input() inErrors: any[];

  destroy$: Subject<boolean> = new Subject<boolean>();
    
  FooForm: FormGroup;

  constructor(
    private formBuilder: FormBuilder,
    public translate: TranslateService,
    ) { 
    }

  get f() { return this.FooForm.controls; }

/////////////////////////////////////////////////////////
////// OnInit & onDestroy
/////////////////////////////////////////////////////////
  ngOnInit(): void {
        this.initForm();
        this.FooForm.valueChanges.takeUntil(this.destroy$).subscribe(value => {
            this.onChange(value);
            this.onTouched();
        });
        
  }

  ngOnDestroy() {
    this.destroy$.next(true);
    this.destroy$.unsubscribe();
  }

//////////////////////////////////////////////////////////////////////////////
///// Control Value Accessor
//////////////////////////////////////////////////////////////////////////////

  get value(): FooFormValues {
    return this.FooForm.value;
  }

  set value(value: FooFormValues) {
    //if( value !== undefined && this.value !== value){ 
    if( value !== undefined ){      
        this.FooForm.patchValue(value);
        this.onChange(value);
        this.onTouched();
    }
  }

  onChange: any = () => {}

  onTouched: any = () => {
  }

  // this method sets the value programmatically
  writeValue(value) {
    if (value) {
        this.value = value;
    }

    if (value === null) {
      this.FooForm.reset();
    }

  }

// upon UI element value changes, this method gets triggered
  registerOnChange(fn) {
    this.onChange = fn;
  }

// upon touching the element, this method gets triggered
  registerOnTouched(fn) {
    this.onTouched = fn;
  }

  // communicate the inner form validation to the parent form
  validate(_: FormControl) {
    return this.FooForm.valid ? null : { profile: { valid: false } };
  }

  get errors() {
    return this.FooForm.errors ? null : this.FooForm.errors;
  }


//////////////////////////////////////////////////////////////////////////////
///// Miscellaneous Functions
//////////////////////////////////////////////////////////////////////////////

  initForm() {
    this.FooForm = this.formBuilder.group({
        id: '',
        toto: ['10', [Validators.required, Validators.min(0)]],
    });
  }

  onSubmitForm() {}

}

My main Form component uses a subscirption to valueChange :

this.mainForm.valueChanges.takeUntil(this.destroy$).subscribe(val => {...});

When add formArray item button is clicked, the main form onChange is triggered, but the val.foos is ["","",...]

I try to add :
    this.value={
        id: null,
        toto: 10,} 

in the InitForm fonction, but same result !

Thank you for your help


Solution

  • Trigger valueChanges manually by calling updateValueandValidity afterViewInit like this it will update parent formControl.

    Try this:

    ngAfterViewInit(){
        setTimeout(()=>{
          this.FooForm.updateValueAndValidity();
        })
      }
    

    Working Example