0

I am implementing dynamic input fields FormArray in Reactive Form to perform the 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

2
  • Your code looks fine, if this.contactdata.employeephones is not blank, you need to check backend then. Commented Sep 23, 2021 at 6:57
  • @RiteshWaghela - When I did console.log(this.contactdata.employeephones); at mapFormValueForContactModel(), I got undefined. But console.log(this.contactdata.current_residential_address) gives me result Commented Sep 23, 2021 at 9:05

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

7 Comments

Note: There is some error(s) in the attached source code, hence the solution on Stackbliz above is to minimally reproduce and fix the error.
Thanks so much. Because of the package I used, How do I select phone_number.e164Number console.log(this.contactdata.employeephones) now gives 0: is_primary_contact_number: true phone_number: countryCode: "NG" dialCode: "+234" e164Number: "+2349099999999" internationalNumber: "+234 909 999 9999" nationalNumber: "0909 999 9999" number: "909 999 9999"
Hi, which phone number will you use? I will update my answer based on it, thanks
I am taking e164Number for the phone_number
Hi, done update my answer, since this.contactInfoForm.value.contacts.phone_number return Object, I believe it is returned from ngx-intl-tel-input, so you need map to get the desired output.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.