Search code examples
angularangular-formbuilder

Grouping a FormArray


I have an input where the user can enter a country and a city separated by a comma to a list of countries and cities. The list should then be displayed in a way that each city is listed under its country group.

I'm struggling to find the right way to construct my form.

form = this.fb.group({
    newEntry: [],
    cityToFind: [],
    citiesList: this.fb.array([]),
});

get citiesListForm(): FormArray {
    return this.form.get('citiesList') as FormArray;
}

get searchInput(): FormControl {
    return this.form.get('cityToFind') as FormControl;
}

get newEntryFormControl(): FormControl {
    return this.form.get('newEntry') as FormControl;
}

getCountryFormArray(country: string): FormArray {
    return this.citiesListForm.get(country) as FormArray;
}

addCountrySection(country: string): void {
    this.citiesListForm.push(
      this.fb.group({
        [country]: this.fb.array([]),
      }),
    );
}

addCity(country: string, city: string): void {
    this.getCountryFormArray(prefix).push(
        this.fb.control(nr),
    );
}

ngOnInit(): void {
    this.addCountrySection('USA');
    this.addCountrySection('DE');
    this.addCity('DE', 'Berlin');
    console.log(this.getCountryFormArray('DE')); // This is returning null
}

Is the form structure correct? Can someone provide me with an example how to write the template.


Solution

  • The major problem with your approach is that you are mixing FormArrays and FormGroups.

    Consider your desired structure

    { 
      citiesList: [         // <--------FormArray
        { de: ["Berlin"] },  // <--------FormGroup
        { usa: [] }
      ]
    }
    

    So If I would like to access the "de" element I would need to do something like

      form[citiesList][0].de
    

    So for the reactive form this would be form.get("citiesList").controls[0].get("de")

    I have illustrated this in This stackblitz demo

    This would work but I think a better approach would be to simply remove the outer array and have below

    { 
      citiesList: { 
        de: ["Berlin"],
        usa: [] 
       }
    }
    
    

    With this you will be adding controls with country as property.

    Another approach to consider may be to use the formArray like below

    { 
      citiesList: [ 
        { country: "DE", cities: ["Berlin"] },
        { country: "USA", cities: [] },
       }
      ]
    }