javascriptangulartypescriptangular-reactive-formsangular2-form-validation

Angular- Reactive Form data not getting saved


I was working on a project with reactive forms. There I added a button 'Submit', clicking which the form was supposed to get submitted. There is also an anchor tag clicking which new fields get added. Now, if I don't add new fields and just fill-up the available fields with data and hit 'Submit' the data get submit(I check them in console.log()). But when I do 'Add More' and add new fields and fill data in the new field and then hit 'Submit', only the data from the previous data get submitted and not the new data fields.

This is the stackbltiz representation of the project

This is the HTML code

<form name="productForm" autocomplete="off" [formGroup]="productForm">
  <div
    class="col-lg-12 col-md-12 col-sm-12 col-12 col-xs-12"
    formArrayName="uom"
    *ngFor="let item of productForm.controls['uom']['controls']; let i = index"
  >
    <div
      [formGroupName]="i"
      class="row col-lg-12 col-md-12 col-sm-12 col-12 col-xs-12 mt-2 p-0"
    >
      {{ i + 1 }}
      <div class="col-lg-2 col-md-2 col-sm-2 col-2 col-xs-12">
        <select
          role="listbox"
          formControlName="type"
          placeholder="Type"
          class="select form-control"
          aria-label="Type"
        >
          <option *ngFor="let list of typeList" [value]="list.tyopeId">
            {{ list.typeName }}
          </option>
        </select>
      </div>
      <div class="col-lg-2 col-md-2 col-sm-2 col-2 col-xs-12">
        <select
          role="listbox"
          formControlName="name"
          placeholder="Name"
          class="select form-control"
          aria-label="Name"
        >
          <option *ngFor="let list of nameList" [value]="list">
            {{ list }}
          </option>
        </select>
      </div>
      <div class="col-lg-2 col-md-2 col-sm-2 col-2 col-xs-12">
        <select
          role="listbox"
          formControlName="desc"
          placeholder="Description"
          class="select form-control"
          aria-label="Description"
        >
          <option *ngFor="let list of descList" [value]="list">
            {{ list }}
          </option>
        </select>
      </div>
    </div>
    ---------------------------
  </div>
  <div style="display:flex;justify-content:space-between;">
    <div class="col-lg-12 col-md-12 col-sm-12 col-12 col-xs-12 mt-2">
      <a href="javascript:void(0)" (click)="addmore()">Add more</a>
    </div>
    <button (click)="onSubmit()">Submit</button>
  </div>
</form>

This is the ts code:

  name = 'Angular';
  productForm: FormGroup;
  typeList;
  nameList;
  descList;

  constructor(
    private formBuilder: FormBuilder,
    private readonly changeDetectorRef: ChangeDetectorRef
  ) {}

  ngOnInit() {
    this.productForm = this.formBuilder.group({
      uom: this.formBuilder.array([this.uom()]),
    });
    this.typeList = [
      {
        tyopeId: 1,
        typeName: 'Type 1',
      },
      {
        tyopeId: 1,
        typeName: 'Type 2',
      },
      {
        tyopeId: 1,
        typeName: 'Type 3',
      },
    ];

    this.nameList = ['Name 1', 'Name 2'];
    this.descList = ['Description 1', 'Description 2'];
  }

  uom() {
    return this.formBuilder.group({
      type: ['', [Validators.required]],
      name: ['', [Validators.required]],
      desc: ['', [Validators.required]],
    });
  }

  // addmore() {
  //   if (!this.productForm.invalid) {
  //     (this.productForm.controls['uom']['controls'] as FormArray).push(
  //       this.uom()
  //     );
  //   } else {
  //     alert('Fill the form');
  //   }
  // }

  addmore() {
    const uomControls = this.productForm.get('uom')['controls'] as FormGroup[];

    for (const control of uomControls) {
      control.markAllAsTouched();
    }

    if (this.productForm.valid) {
      (this.productForm.controls['uom']['controls'] as FormArray).push(
        this.uom()
      );
    } else {
      alert('Fill the form');
    }
  }

  onSubmit() {
    this.productForm.markAllAsTouched(); // Mark all form controls as touched

    if (this.productForm.valid) {
      console.log('submit =>', this.productForm.controls['uom']['controls']);
    } else {
      alert('Fill the form');
    }
  }

  ngAfterViewChecked(): void {
    this.changeDetectorRef.detectChanges();
  }

As you can see here,

  onSubmit() {
    this.productForm.markAllAsTouched(); // Mark all form controls as touched

    if (this.productForm.valid) {
      console.log('submit =>', this.productForm.controls['uom']['controls']);
    } else {
      alert('Fill the form');
    }
  }

I am using alert to check if the data is getting submitted on not. I have used this function this.productForm.valid to check if all the form is filled or not. But even when the fields are filled,the alert is still getting triggered.


Solution

  • In the addMore method you are pushing the new FormGroup directly in the controls array instead of the FormArray.

     addmore() {
       ...
    
       if (this.productForm.valid) {
         (this.productForm.controls['uom']['controls'] as FormArray).push(...) <----
    
      ...
    }
    

    Just remove the last [controls] and it should work as you expect.

    (this.productForm.controls['uom'] as FormArray).push(...)