Search code examples
angulartypescriptangular-formsangular-formbuilder

Angular Dynamic Reactive Form Nested FormArray for Radios Buttons


On API call I have an Array of questions and their options JSON.

 [
  {
    "QuestionText": "Question goes here...",
    "AnswerChoice": [
      {
        "text": "text1",
        "answerId": "1"
      },
      {
        "text": "text2",
        "answerId": "2"
      },
      {
        "text": "text3",
        "answerId": "3"
      },
      {
        "text": "text4",
        "answerId": "4"
      },
      {
        "text": "text5",
        "answerId": "5"
      }
    ],
    "questionId": "1"
  },
  {
    "QuestionText": "Second question goes here...?",
    "AnswerChoice": [
      {
        "text": "text1",
        "answerId": "1"
      },
      {
        "text": "text2",
        "answerId": "2"
      },
      {
        "text": "text3",
        "answerId": "3"
      },
      {
        "text": "text4",
        "answerId": "4"
      },
      {
        "text": "text5",
        "answerId": "5"
      }
    ],
    "questionId": "2"
  }
  ... and so on.
]

Answer choices are radio buttons in UI.

So, now the question.

I am trying to build a Reactive Form for this problem. I am not able to come up with a solution. I am trying to build nested FormArrays but in vain.

My attempts to solve this problem:

HTML -

        <form [formGroup]="eidFormSubmission">
        <div>
            <div *ngFor="let question of questions; let i = index">
                <p>
                    {{i+1}}. {{question.QuestionText}}
                </p>
                <div class="form-group">
                    <div>
                        <div class="custom-control custom-radio"
                            *ngFor="let answer of question.AnswerChoice let k=index">
                            {{question.questionId}}
                            <input [value]="answer.answerId" type="radio" formControlName="i"
                                id="{{question.questionId}}" name="quesAnswer"
                                class="custom-control-input">
                            <label class="custom-control-label" for="{{question.questionId}}">
                                {{ answer.text }}</label>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <button (click)="onSubmitAnswers()" class="btn btn-primary">Next</button>
    </form>

TS -

  eidFormSubmissionInit(){
    const formArray = this.formBuilder.array([]);
    this.eidFormSubmission = this.formBuilder.group({
      questions: formArray
    })
  }

Now I am confused about how to push dynamically (after API response) into formbuilder.

And second problem is with the radio button when I select an option from the second question it deselects from the first question.

Any help would be appreciated!


Solution

  • Check this:

    First create a form like this with "questions" inside as a FormArray:

    this.eidFormSubmission = this.fb.group({
      questions: this.fb.array([])
    });
    

    after in your ngOnInit when your receive questions, loop through each question and add a new item into "questions" formArray

      ngOnInit() {
        this.dataService.getQuestions().subscribe((data: any[]) => {
          console.log(data);
          this.questions = data;
    
          this.questions.forEach(question => {
            (this.eidFormSubmission.get('questions') as FormArray).push(this.addQuestionFormGroup(question));
          });
        });
      }
    

    with this utility function:

      private addQuestionFormGroup(question: any): FormGroup {
        return this.fb.group({
          QuestionText: question.QuestionText,
          AnswerChoice: new FormControl()
        });
      }
    

    html markup:

    <form (ngSubmit)="onSubmitAnswers()" [formGroup]="eidFormSubmission">
      <table>
        <tr formArrayName="questions" *ngFor="let data of eidFormSubmission.get('questions').controls; let i = index">
          <ng-container [formGroupName]="i">
            <th>
              <input type="text" formControlName="QuestionText" readonly>
            </th>
            <th *ngFor="let answer of questions[i].AnswerChoice">
              <input type="radio" formControlName="AnswerChoice" value="{{answer.answerId}}">{{answer.text}}
            </th>
          </ng-container>
        </tr>
      </table>
      <br>
      <button type="submit">Submit</button>
    </form>
    

    Working stackblitz