Search code examples
javascriptangulartypescriptprimengangular-forms

Nested FormGroup in FormArray repeating all radio button values - Angular (UI with PrimeNG)


Stackblitz

I am working on a form with radio button inputs (using PrimeNg ui but this shouldn't matter to the question at hand), where each radio button group is being populated by an API into a form group. In my AppComponent I am populating the data, and is how this comes in from the API (static data for sample):

ngOnInit() {
    this.settingsByCategory = [
      {
        categoryName: 'None',
        permissions: [
          {
            id: 1,
            name: 'Test Category 1'
          },
          {
            id: 2,
            name: 'Test Category 2'
          },
          {
            id: 9,
            name: 'Test Category 3'
          }
        ]
      }
    ];

    this.settingForm = this.fb.group({
      settings: this.fb.array([])
    });

    this.setForm();
  }

  setForm() {
    this.settingsByCategory.forEach(i => {
      i.permissions.forEach(p => {
        (this.settingForm.get('settings') as FormArray).push(
          this.fb.group({
            settingId: p.id,
            settingName: p.name,
            permission1: null,
            permission2: null,
            permission3: null
          })
        );
      });
    });
  }

The problem is that when I select a radio button, all groups are selected instead of just one single group.
enter image description here

In my HTML I am using a nested grouping of FormGroup, FormArrayName, and FormControls to populate with the appropriate data. It is a little messy, but I am first trying to work through this issue:

<ng-container>
  <div>
    <form [formGroup]="settingForm">
      <div formArrayName="settings" class="perm-grid">
        <div *ngFor="let setting of settings.controls; index as i">
          <div [formGroupName]="i">
            <h2 style="border-bottom: 1px solid #495057; padding-left: 5px">{{ setting.value.settingName }}</h2>
            <ul class="perm-list">
              <li class="radio-grid">
                <div class="radio-grid-row">
                  <div style="margin-left: 5px"></div>
                  <div>Company</div>
                  <div>Area</div>
                  <div>Location</div>
                </div>
                <div class="radio-grid-row">
                  <div style="margin-left: 5px">None</div>
                  <div>
                    <p-radioButton (onClick)="logRadio($event, setting)" name="permission1"
                      formControlName="permission1" [value]=0>
                    </p-radioButton>
                  </div>
                  <div>
                    <p-radioButton (onClick)="logRadio($event, setting)" name="permission2"
                      formControlName="permission2" [value]=0>
                    </p-radioButton>
                  </div>
                  <div>
                    <p-radioButton (onClick)="logRadio($event, setting)" name="permission3"
                      formControlName="permission3" [value]=0>
                    </p-radioButton>
                  </div>
                </div>
                <div class="radio-grid-row">
                  <div style="margin-left: 5px">View</div>
                  <div>
                    <p-radioButton (onClick)="logRadio($event, setting)" name="permission1"
                      formControlName="permission1" [value]=1>
                    </p-radioButton>
                  </div>
                  <div>
                    <p-radioButton (onClick)="logRadio($event, setting)" name="permission2"
                      formControlName="permission2" [value]=1>
                    </p-radioButton>
                  </div>
                  <div>
                    <p-radioButton (onClick)="logRadio($event, setting)" name="permission3"
                      formControlName="permission3" [value]=1>
                    </p-radioButton>
                  </div>
                </div>
                <div class="radio-grid-row">
                  <div style="margin-left: 5px">Edit</div>
                  <div>
                    <p-radioButton (onClick)="logRadio($event, setting)" name="permission1"
                      formControlName="permission1" [value]=2>
                    </p-radioButton>
                  </div>
                  <div>
                    <p-radioButton (onClick)="logRadio($event, setting)" name="permission2"
                      formControlName="permission2" [value]=2>
                    </p-radioButton>
                  </div>
                  <div>
                    <p-radioButton (onClick)="logRadio($event, setting)" name="permission3"
                      formControlName="permission3" [value]=2>
                    </p-radioButton>
                  </div>
                </div>
              </li>
            </ul>
          </div>
        </div>
      </div>
    </form>
  </div>
</ng-container>

With standard inputs, I have used this method and it has worked just fine. Why are all the radiobutton form controls being repeated across groups?

Stackblitz


Solution

  • That happens because the name of the radio-button group is the same for all categories, so all of them are connected to each other.

    You can resolve this issue like the following:

    • Add a suffix/prefix to the group name to distinguish them from each other, e.g. you can add the index i such as name="permission1-{{i}}", so this will be resolved later to name="permission1-0" for category 1 & name="permission1-1" for category 2...etc.
    • The above change will cause another problem related to the used primeNG version, which is: If you define both a name and a formControlName attribute on your radio button, their values must match, and to resolve it you can use formControl instead of formControlName such as: [formControl]="settingForm?.get('settings').controls[i].get('permission1')"

    So the radio-botton part of your component template will look like the following:

    <div class="radio-grid-row">
      <div style="margin-left: 5px">None</div>
      <div>
        <p-radioButton (onClick)="logRadio($event, setting)" name="permission1-{{i}}"
          [formControl]="settingForm?.get('settings').controls[i].get('permission1')" [value]="0">
        </p-radioButton>
      </div>
      <div>
        <p-radioButton (onClick)="logRadio($event, setting)" name="permission2-{{i}}"
          [formControl]="settingForm?.get('settings').controls[i].get('permission2')" [value]="0">
        </p-radioButton>
      </div>
      <div>
        <p-radioButton (onClick)="logRadio($event, setting)" name="permission3-{{i}}"
          [formControl]="settingForm?.get('settings').controls[i].get('permission3')" [value]="0">
        </p-radioButton>
      </div>
    </div>
    <div class="radio-grid-row">
      <div style="margin-left: 5px">View</div>
      <div>
        <p-radioButton (onClick)="logRadio($event, setting)" name="permission1-{{i}}"
          [formControl]="settingForm?.get('settings').controls[i].get('permission1')" [value]="1">
        </p-radioButton>
    
      </div>
      <div>
        <p-radioButton (onClick)="logRadio($event, setting)" name="permission2-{{i}}"
          [formControl]="settingForm?.get('settings').controls[i].get('permission2')" [value]="1">
        </p-radioButton>
      </div>
      <div>
        <p-radioButton (onClick)="logRadio($event, setting)" name="permission3-{{i}}"
          [formControl]="settingForm?.get('settings').controls[i].get('permission3')" [value]="1">
        </p-radioButton>
      </div>
    </div>
    <div class="radio-grid-row">
      <div style="margin-left: 5px">Edit</div>
      <div>
        <p-radioButton (onClick)="logRadio($event, setting)" name="permission1-{{i}}"
          [formControl]="settingForm?.get('settings').controls[i].get('permission1')" [value]="2">
        </p-radioButton>
      </div>
      <div>
        <p-radioButton (onClick)="logRadio($event, setting)" name="permission2-{{i}}"
          [formControl]="settingForm?.get('settings').controls[i].get('permission2')" [value]="2">
        </p-radioButton>
      </div>
      <div>
        <p-radioButton (onClick)="logRadio($event, setting)" name="permission3-{{i}}"
          [formControl]="settingForm?.get('settings').controls[i].get('permission3')" [value]="2">
        </p-radioButton>
      </div>
    </div>
    

    And here is the edited version of your stackbiltz: https://stackblitz.com/edit/primeng-dynamicdialog-demo-25iydv