Home > OS >  Angular - Dynamic input form array fails to update
Angular - Dynamic input form array fails to update

Time:09-23

I am implementing dynamic input fields FormArray in Reactive Form to perform update in Angular-12. I have this code:

Interface:

export interface IResponse<T> {
  message: string;
  error: boolean;
  code: number;
  results: T;
}

export interface IEmployees {
  employees: IEmployee[];
}

export class EmployeeResponse {
  results!: { employee: IEmployee; };
}

export interface IEmployee {
  id?: number;
  current_residential_address?: string;
  employeephones?: IContact[];
}

export interface IContact {
  id?: number;
  phone_number: string;
  phone_type_id?: number;
  phonetypes?: {id:number,type_name:string};
  is_primary_contact_number?: boolean;
}

service

getContactById(id: number): Observable<EmployeeResponse> {
  return this.http.get<EmployeeResponse>(this.api.baseURL   'company/employees/fetchbyid/'   id, this.httpOptions);
}

public updateContact(id: number, employee: IEmployee): Observable<any> {
  return this.http.post(this.api.baseURL   'employees/contact/update/'   id, employee, this.httpOptions);
}

Component:

isLoading = false;
isSubmitted = false;
contactdata!: IEmployee;
contactInfoForm!: FormGroup;

ngOnInit(): void {
  this.isLoading = true;
  this._id = this.route.snapshot.params['id'];
  this.updateContact();
  this.loadContactById();
}

loadContactById() {
  this.employeeService
    .getContactById(this._id)
    .subscribe((data: EmployeeResponse) => {
      this.contactdata = data.results.employee;
      this.contactInfoForm.patchValue({
        current_residential_address: this.contactdata.current_residential_address,
      });
      this.contactInfoForm.setControl(
        'contacts',
        this.SetExistingContacts(this.contactdata.employeephones || [])
      );
    });
}

SetExistingContacts(contactSets: IContact[]): FormArray {
  const formarray = new FormArray([]);
  contactSets.forEach(c => {
    formarray.push(this.fb.group({
      phone_number: c.phone_number,
      phone_type_id: c.phone_type_id,
      is_primary_contact_number: c.is_primary_contact_number
    }));
  });
  return formarray;
}

updateContact() {
  this.contactInfoForm = this.fb.group({
    id: [''],
    current_residential_address: ['', [Validators.required]],
    contacts: this.fb.array([
      this.addContactFormGroup()
    ])
  });
}

addContactFormGroup(): FormGroup {
  return this.fb.group({
    phone_type_id: ['', Validators.required],
    phone_number: ['', [Validators.required, Validators.maxLength(15)]],
    is_primary_contact_number: ['']
  });
}

get fc() {
  return this.contactInfoForm.controls;
};

public addContactButtonClick() {
  const contacts = this.contactInfoForm.get('contacts') as FormArray
  contacts.push(this.addContactFormGroup())
}

get contacts() {
  return this.contactInfoForm.controls['contacts'] as FormArray;
}

getContactsFormArray(): FormArray {
  return this.contactInfoForm.get('contacts') as FormArray;
}

get contactArray(): FormArray {
  return <FormArray > this.contactInfoForm.get('contacts');
}

onSubmitContact() {
  this.isSubmitted = true;

  if (this.contactInfoForm.invalid) {
    return;
  }
  this.isLoading = true;
  this.mapFormValueForContactModel();
  this.employeeService.updateContact(this._id, this.contactdata).subscribe(res => {
    this.data = res;
  });
}

mapFormValueForContactModel() {
  this.contactdata.current_residential_address = this.contactInfoForm.value.current_residential_address;
  this.contactdata.employeephones = this.contactInfoForm.value.employeephones;
}
<form [formGroup]="contactInfoForm" (ngSubmit)="onSubmitContact()">
  <div class="row">
    <div class="col-12 col-md-12">
      <div class="form-group">
        <label for="current_residential_address">Current Residential Address:<span style="color:red;">*</span></label>
        <textarea rows="2" formControlName="current_residential_address" name="description" type="text" placeholder="22, Alexander Close ..." class="form-control mb-3" required>
                              </textarea>
      </div>
      <div *ngIf="fc.current_residential_address.touched && fc.current_residential_address.invalid">
        <div *ngIf="fc.current_residential_address.hasError('required')">
          <div class="text-danger">
            Current Residential Address is required!
          </div>
        </div>
      </div>
    </div>
  </div>
  <div class="row">
    <div formArrayName="contacts" class="col-md-12">
      <div *ngFor="let contact of getContactsFormArray().controls; let i = index" [formGroupName]="i">
        <p>
          <b>Contact Phone : {{i   1}}</b>
        </p>
        <hr>
        <div class="row">
          <div class="col-12 col-md-4">
            <div class="form-group">
              <label for="phone_number">Phone Number:<span style="color:red;">*</span></label>
              <div class="input-group mb-4">
                <ngx-intl-tel-input [cssClass]="'form-control mb-4'" [preferredCountries]="preferredCountries" [enableAutoCountrySelect]="false" [enablePlaceholder]="true" [searchCountryFlag]="true" [searchCountryField]="[SearchCountryField.Iso2, SearchCountryField.Name]"
                  [selectFirstCountry]="false" [selectedCountryISO]="CountryISO.Scotland" [phoneValidation]="true" [separateDialCode]="true" name="phone_number" formControlName="phone_number">
                </ngx-intl-tel-input>
              </div>
            </div>
            <div *ngIf="getContactFormGroup(i).get('phone_number')!.touched && getContactFormGroup(i).get('phone_number')!.invalid">
              <div *ngIf="getContactFormGroup(i).get('phone_number')!.hasError('required')">
                <div class="text-danger">
                  Phone Number is required!
                </div>
              </div>
              <div *ngIf="getContactFormGroup(i).get('phone_number')!.hasError('validatePhoneNumber')">
                <div class="text-danger">
                  Invalid Phone Number!
                </div>
              </div>
            </div>
          </div>
          <div class="col-12 col-md-4">
            <div class="form-group">
              <label for="phone_type_id">Phone Type:<span style="color:red;">*</span></label>
              <ng-select [items]="phonetypes" [selectOnTab]="true" [searchable]="true" bindValue="id" bindLabel="type_name" placeholder="Select Phone Type" [multiple]="false" [clearable]="true" required formControlName="phone_type_id">
              </ng-select>
            </div>
          </div>
          <div class="col-12 col-md-2">
            <div class="form-group">
              <br><button type="button" class="btn btn-danger float-right" (click)="removeOrClearContact(i)"><i class="fas fa-times-circle"></i> Remove</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <div class="card-footer">
    <button type="button" class="btn btn-primary float-right" (click)="addContactButtonClick()"><i class="fas fa-plus-circle"></i> Add</button>
    <button type="submit" class="btn btn-success" [disabled]="isLoading" class="btn btn-success" (click)="contactValidate()">
                      <span *ngIf="isLoading" class="spinner-border spinner-border-sm mr-1"></span>
                      <i class="fa fa-save" aria-hidden="true"></i> Save</button>
  </div>
</form>

I have two sets of data: single data(current_residential_address) and dynamic form array

When I submitted to update the data, only current_residential_address was updated but the array (contacts) was not.

How do I correct this?

Thanks

CodePudding user response:

In your FormGroup, there is no employeephones FormArray, but it is contacts FormArray.

updateContact() {
  this.contactInfoForm = this.fb.group({
    id: [''],
    current_residential_address: ['', [Validators.required]],
    contacts: this.fb.array([this.addContactFormGroup()]),
  });
}
mapFormValueForContactModel() {
  this.contactdata.current_residential_address = this.contactInfoForm.value.current_residential_address;
  this.contactdata.employeephones = this.contactInfoForm.value.employeephones;
}

Solution

Replace this.contactInfoForm.value.employeephones with this.contactInfoForm.value.contacts.

And map this.contactInfoForm.value.contacts to return desired output with phone_number as phone_number from FormGroup was returned with an object (contains multiple type of phone numbers) by ngx-intl-tel-input.

mapFormValueForContactModel() {
  this.contactdata.current_residential_address =
      this.contactInfoForm.value.current_residential_address;
  this.contactdata.employeephones =
  this.contactInfoForm.value.contacts.map(
      (value) => {
        return {
          phone_type_id: value.phone_type_id,
          is_primary_contact_number: value.is_primary_contact_number,
          phone_number: value.phone_number.e164Number
        };
      }
    );
}

Sample Solution on StackBlitz

  • Related