4

I have the following code that dynamically adds a form group containing a title and two radio buttons to create a condition, effectively providing its title and whether or not it's an acceptable condition:

export enum Acceptability {
  ACCEPTABLE,
  INACCEPTABLE
}

export interface Condition {
  title: string;
  acceptability: Acceptability;
}

export class AddCondition implements OnInit {

  form: FormGroup;
  ACCEPTABILITY = Acceptability;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    // build the form
    this.form = this.fb.group({
      conditions: this.fb.array([])
    });

  }

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

  addCondition() {
    this.conditions.push(this.fb.group({
      title: ['', Validators.required],
      acceptability: ['', Validators.required]
    }));
  }
}
<div formArrayName="conditions">
  <div *ngFor="let condition of conditions.controls; let i=index" [formGroupName]="i">
    <input class="form-control" formControlName="title" type="text" placeholder="title">
    <div class="custom-control custom-radio custom-control-inline">
      <input type="radio" id="acceptable" formControlName="acceptability" name="acceptability" class="custom-control-input" [value]="ACCEPTABILITY.ACCEPTABLE">
      <label class="custom-control-label" for="acceptable">Acceptable</label>
    </div>
    <div class="custom-control custom-radio custom-control-inline">
      <input type="radio" id="inacceptable" formControlName="acceptability" name="acceptability" class="custom-control-input" [value]="ACCEPTABILITY.INACCEPTABLE">
      <label class="custom-control-label" for="inacceptable">Inacceptable</label>
    </div>
  </div>
  <button type="button" class="btn btn-outline-dark btn-block" (click)="addCondition()">
          <i class="fa fa-plus fa-lg mr-3"></i>ADD A NEW CONDITION</button>
</div>

The problem is that when clicking the ADD A NEW CONDITION button, and the new form group appears, every time I select any radio button in the group array, the corresponding radio button of the first array item in the group is the one that gets selected.

Sammy
  • 3,395
  • 7
  • 49
  • 95

2 Answers2

7

What you need to consider when looping through an array group is the id and name you assign to each one. You currently have your input id and name getting set to the same value in each loop.

You would need to use the i variable you have available to make the id and name for each input a dynamic value, like below:

<div [formGroup]="form">
  <div *ngIf="conditions" formArrayName="conditions">

    <div *ngFor="let condition of conditions.controls; let i=index" [formGroupName]="i">

      <input class="form-control" formControlName="title" type="text" placeholder="title">

      <div class="custom-control custom-radio custom-control-inline">
        <input type="radio" id="{{ 'acceptable' + i}}" formControlName="acceptability"
            class="custom-control-input" [value]="ACCEPTABILITY.ACCEPTABLE">
        <label class="custom-control-label" for="{{ 'acceptable' + i}}">Acceptable</label>
      </div>

      <div class="custom-control custom-radio custom-control-inline">
        <input type="radio" id="{{ 'unacceptable' + i}}" formControlName="acceptability"
            class="custom-control-input" [value]="ACCEPTABILITY.INACCEPTABLE">
        <label class="custom-control-label" for="{{ 'unacceptable' + i}}">Unacceptable</label>
      </div>

    </div>

    <button type="button" class="btn btn-outline-dark btn-block" (click)="addCondition()">
            <i class="fa fa-plus fa-lg mr-3"></i>ADD A NEW CONDITION</button>
  </div>
</div>

<div>
  <pre>{{ form.value | json }}</pre>
</div>

Notice the string interpolation for the name and id attributes. Same goes for the for attribute in the labels.

You may have to play around with this to get is exactly where it needs to be. (I didn't test it thoroughly)

Hope this helps.

R. Richards
  • 24,603
  • 10
  • 64
  • 64
  • Thank you, but this then produces the following error: `If you define both a name and a formControlName attribute on your radio button, their values must match. Ex: ` – Sammy Jan 14 '18 at 17:52
  • But then of course, if I go ahead and change `formControlName` to match it, then I get the error that the control does not exist; which is normal since when I push the new group, I push it with the name `acceptability` only. – Sammy Jan 14 '18 at 17:53
  • Have you tried it without setting the `name` attribute at all by removing it? – R. Richards Jan 14 '18 at 17:58
  • Yes, that's where I get `Error: Cannot find control with path: 'conditions -> 0 -> acceptability0'` which is, I believe, logical, since what I'm `push`ing up there is `acceptability` only. – Sammy Jan 14 '18 at 17:59
  • 1
    Okay, let me try a few things. I will update the answer when I have something. Sorry about the back and forth! – R. Richards Jan 14 '18 at 18:03
  • I have updated the answer. This is working for me. No errors at all. I am able to add to the form array and the radio buttons work as expected. I added a div tag to output the form value, which looks accurate to me, and update when the form values change. Have a look and give this a try. – R. Richards Jan 14 '18 at 18:32
  • Thank you; besides removing `name`, did you do anything else that I'm not seeing? When you click the "add" button, you do not get the `Cannot find control with path...` error? – Sammy Jan 14 '18 at 18:34
  • No errors whatsoever. Removing the name was all I really did from the code I originally posted. I pulled your component code into a project I have been playing around with to test this. Works. – R. Richards Jan 14 '18 at 18:38
  • 1
    Indeed; I guess removing name and *not* having `formControlName` dynamic was the one permutation I did not try. Thank you :) – Sammy Jan 14 '18 at 18:41
  • This helped alot thank you, I was stuck on it for a while and couldn't work out why. I did have to remove the '[]' expression syntax from my 'value' attribute however. But everything else worked great. Thanks! – IronWorkshop Jan 12 '20 at 20:40
0

I seems that you missed this line id="{{ 'unacceptable' + i}}" for id & label both.

a.ak
  • 659
  • 2
  • 12
  • 26