Shinchan
Shinchan

Reputation: 103

How to patch values for a dynamic form array in Angular 10?

I have a dynamic formArray, saving the data and all is working fine but I am having issue patching values, also I couldn't find any exact solutions online.

Below is the code,

    <form [formGroup]="educationAndExperienceForm">
        <div formArrayName="educationForm">
            <div *ngFor="let education of educationAndExperienceForm.controls.educationForm['controls'];let i = index;"
                [formGroupName]="i">
                <h6 class="text-left circularBold" *ngIf="i == 0"><b>Highest Qualification</b></h6>
                <input type="text" class="education-inputs w-100 mt-3" placeholder="School/College/University"
                    formControlName="school"
                    [ngClass]="{ 'input-error': submitted && educationAndExperienceForm.controls.educationForm.controls[i].invalid }">
                <input type="text" class="education-inputs w-100 mt-3" placeholder="Degree Ex: Master's"
                    formControlName="degree"
                    [ngClass]="{ 'input-error': submitted && educationAndExperienceForm.controls.educationForm.controls[i].invalid }">
                <input type="text" class="education-inputs w-100 mt-3"
                    placeholder="Field of Education Ex: Computer Science" formControlName="specialization"
                    [ngClass]="{ 'input-error': submitted && educationAndExperienceForm.controls.educationForm.controls[i].invalid }">
                <div class="text-left">
                    <input type="text" class="education-inputs w-100 mt-3" placeholder="Graduate Year"
                        formControlName="passed_year"
                        [ngClass]="{ 'input-error': submitted && educationAndExperienceForm.controls.educationForm.controls[i].invalid }">
                </div>
                <div class="row text-right">
                    <div class="col-10"></div>
                    <div class="col-1">
                        <button type="button" class="btn deleteBtn" (click)="removeEducationFormGroup()">
                            <i class="fa fa-remove" style="font-size: 24px;" aria-hidden="true"></i>
                        </button>
                    </div>
                </div>
                <div class="text-left" *ngIf="!showAddAfterEducationHeading">
                    <button type="button" class="btn btn-light circularMedium mb-3 transparentBtn"
                        (click)="addEducationFormGroup()">
                        Add Education
                    </button>
                </div>
            </div>
            <div class="text-left" *ngIf="showAddAfterEducationHeading">
                <button type="button" class="btn btn-light circularMedium mb-3 transparentBtn"
                    (click)="addEducationFormGroup()">
                    Add Education
                </button>
            </div>
        </div>
</form>

TS File

this.educationAndExperienceForm = this.fb.group({
      'educationForm': this.fb.array([this.createEducationFormGroup()])
});

private createEducationFormGroup(): FormGroup {
    return this.educationForm = this.fb.group({
      'school': ['', Validators.required],
      'degree': ['', Validators.required],
      'specialization': ['', Validators.required],
      'passed_year': ['', Validators.required]
    })
  }

Below is the code I tried for patching values,

1. One way

fetchQualificationById(){
    this.apiService.fetchQualificationById(this.expertId).subscribe(res => {
      console.log("Response Ed = "+JSON.stringify(res));
      this.qualificationData = res['expertsQualification'];
     });
    let school = this.qualificationData['school'];
    let degree = this.qualificationData['degree'];
    let specialization = this.qualificationData['specialization'];
    let passed_year = this.qualificationData['passed_year'];
    this.educationAndExperienceForm.controls.educationForm.patchValue({
      'school': school,
      'degree': degree,
      'specialization': specialization,
      'passed_year': passed_year
    })
    // console.log("Get Qualification = "+JSON.stringify(this.educationAndExperienceForm.controls.educationForm.value))
  }

2. Second way

 

fetchQualificationById(){
    this.apiService.fetchQualificationById(this.expertId).subscribe(res => {
      console.log("Response Ed = "+JSON.stringify(res));
      this.qualificationData = res['expertsQualification'];
      for (let education=0; education < res['expertsQualification'].length; education++){
        const educationFormArray = this.educationAndExperienceForm.get("educationForm") as FormArray;
     }
     });
}

For the first way I use I'm getting cannot use forEach.. error

Edited

A

Here there's only one array but we could one empty array, why is that? Below is my ts code

@Component({
  selector: 'app-expert-education',
  templateUrl: './expert-education.component.html',
  styleUrls: ['./expert-education.component.css']
})
export class ExpertEducationComponent implements OnInit {

  educationForm: FormGroup;
  educationAndExperienceForm: FormGroup;
  experienceForm: FormGroup;
  submitted: boolean;
  showAddAfterEducationHeading: boolean;
  showAddAfterExperienceHeading: boolean;
  expertId;
  qualificationData: QualificationModel[] = [];
  experienceData: ExperienceModel[] = [];

  constructor(
    private route: Router,
    private fb: FormBuilder,
    private apiService: ApiService
  ) {
    this.submitted = false;
  }

  ngOnInit(): void {
    // this.educationForm = this.fb.group({
    //   'school': ['', Validators.required],
    //   'degree': ['', Validators.required],
    //   'field': ['', Validators.required],
    //   'graduatedYear': ['', Validators.required],
    //   'experience': ['', Validators.required],
    //   'designation': ['', Validators.required],
    //   'company': ['', Validators.required]
    // })
    this.expertId = sessionStorage.getItem('id_expert');
    this.educationAndExperienceForm = this.fb.group({
      'educationForm': this.fb.array([this.createEducationFormGroup()]),
      'experienceForm': this.fb.array([this.createExperienceFormGroup()])
    });
    this.showAddAfterEducationHeading = false;
    this.showAddAfterExperienceHeading = true;
    this.fetchQualificationById();
    this.fetchExperienceById();
  }

  addEducationFormGroup() {
    this.showAddAfterEducationHeading = true;
    const education = this.educationAndExperienceForm.get('educationForm') as FormArray;
    education.push(this.createEducationFormGroup());
  }

  addExperienceFormGroup() {
    this.showAddAfterExperienceHeading = true;
    const experience = this.educationAndExperienceForm.get('experienceForm') as FormArray;
    experience.push(this.createExperienceFormGroup());
  }

  removeEducationFormGroup(index) {
    const education = this.educationAndExperienceForm.get('educationForm') as FormArray
    if (education.length > 1) {
      education.removeAt(index);
    } else {
      education.reset();
    }
  }

  removeExperienceFormGroup(index) {
    const experience = this.educationAndExperienceForm.get('experienceForm') as FormArray
    if (experience.length > 1) {
      experience.removeAt(index);
    } else {
      experience.reset();
    }
  }

  private createEducationFormGroup(): FormGroup {
    return this.educationForm = this.fb.group({
      'school': ['', Validators.required],
      'degree': ['', Validators.required],
      'specialization': ['', Validators.required],
      'passed_year': ['', Validators.required]
    })
  }

  private createExperienceFormGroup(): FormGroup {
    return this.experienceForm = this.fb.group({
      'duration': ['', Validators.required],
      'designation': ['', Validators.required],
      'company_name': ['', Validators.required]
    })
  }

  onSubmit() {
    this.submitted = true;
    console.log("Form = " + JSON.stringify(this.educationAndExperienceForm.controls.educationForm.value) + "\nValid = " + this.educationAndExperienceForm.valid)
    if (this.educationAndExperienceForm.valid) {
      this.apiService.saveExpertEducationAndExperience(this.educationAndExperienceForm.controls.educationForm.value, this.educationAndExperienceForm.controls.experienceForm.value, this.expertId).subscribe(res => {
        console.log("Expert education & exp res = " + JSON.stringify(res));
        this.route.navigate(['/expert-welcome/expert-services']);
      })
    }
  }

  back() {
    this.route.navigate(['/expert-welcome/expert-profile']);
  }

  fetchQualificationById() {
    this.apiService.fetchQualificationById(this.expertId).subscribe(res => {
      this.qualificationData = res['expertsQualification'];
      const control = <FormArray>this.educationAndExperienceForm.controls['educationForm'];
      this.qualificationData.forEach(x => {
        control.push(this.fb.group({
          school: [x.school, Validators.required],
          degree: [x.degree, Validators.required],
          specialization: [x.specialization, Validators.required],
          passed_year: [x.passed_year, Validators.required]
        }));
      });
    });
  }

  fetchExperienceById() {
    this.apiService.fetchExperienceById(this.expertId).subscribe(res => {
      this.experienceData = res['expertExperience'];
      const control = <FormArray>this.educationAndExperienceForm.controls['experienceForm'];
      this.experienceData.forEach(x => {
        control.push(this.fb.group({
          duration: [x.duration, Validators.required],
          designation: [x.designation, Validators.required],
          company_name: [x.company_name, Validators.required],
        }));
      });
    });
  }

}

Any help is appreciated.

Upvotes: 2

Views: 1625

Answers (1)

Alex
Alex

Reputation: 434

The problem seems that patchValue is not able to fill formarrays. You can fill formarrays like regular arrays with push().

Assuming that your this.qualificationData is an array containing many "expertsQualification"s (type Array) and the class/interface ExpertsQualification has the properties "school", "degree", "specialization" and "passed_year" you could do the following:

(this.educationAndExperienceForm.controls.educationForm as FormArray).reset(); //reset the FormArray

this.qualificationData.forEach(x => {
    (this.educationAndExperienceForm.controls.educationForm as FormArray)
    .push(this.fb.group({
        school: [x.school, Validators.required],
        degree: [x.degree, Validators.required],
        specialization: [x.specialization, Validators.required],
        passed_year: [x.passed_year, Validators.required]
    });
};

.. so that you loop through your array and for each element you push a formgroup to the formarray. Please note tht it's very important to cast it to FormArray like

(this.educationAndExperienceForm.controls.educationForm as FormArray)

If you don't do this, it will stay an AbstractControl and you won't be able to use the array functions like push, pull etc.

Upvotes: 5

Related Questions