Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to represent Array using fieldset in angular template driven form model

Tags:

angular6

I have a form which is having nested objects array. For representing multi level form view model we are using ngModelGroup with which we can map parent child relation. Is there a way to represent array using a directive something similar to ngModelGroup?

below are the models,

export class SurveyViewModel {
Survey: Survey;
QuestionAnswerSets: QuestionAnswerSet[];
}

export class Survey {
Id: string;
Name: string;   
}


export class QuestionAnswerSet {
Question: Question;
Answers: Answer[];
}

below is the view,

  <form [formGroup]="surveyForm" (ngSubmit)="onFormSubmit(surveyForm)">

      <div formGroupName="Survey">
        <input type="hidden" name="Id" formControlName="Id">
        <label>{{model.Survey.Name}}</label>
      </div>

      <div class='qa-set' formArrayName="QuestionAnswerSets">
        <fieldset *ngFor="let questionAnswerSet of surveyForm.controls.QuestionAnswerSets.controls; index as setId;" [formGroupName]="setId">
          <div formGroupName="Question">

            <input type="hidden" name="Id" formControlName="Id">
            <label>{{setId+1}}. </label>
            <label>{{model.QuestionAnswerSets[setId].Question.Value}}</label>
          </div>

          <div class="row" formArrayName="Answers">
            <div *ngFor="let answer of this.surveyForm.controls.QuestionAnswerSets.controls[setId].controls.Answers.controls; index as answerId;"
              [formGroupName]="answerId">

              <div class="col-12 col-sm-3" formGroupName="Answer">
                <input type="hidden" name="Id" formControlName="Id">
                <label>
                  <input type="radio" name="Value" formControlName="Value" /> {{model.QuestionAnswerSets[setId].Answers[answerId].Value}}
                </label>

              </div>

            </div>
          </div>

        </fieldset>
      </div>

      <div class="row form-group">
        <div class="col-12">
          <button type="button" class="btn btn-primary float-right" type="submit">submit</button>
        </div>
      </div>
    </form>
like image 957
Mahesh Avatar asked Jul 23 '18 18:07

Mahesh


2 Answers

This is how you can do it with FormArray

component

import { Component, OnInit } from "@angular/core";
import {
  Validators,
  FormBuilder,
  FormGroup,
  FormControl,
  FormArray
} from "@angular/forms";
@Component({
  ...
})
export class SurveyComponent implements OnInit {
  public surveyForm: FormGroup;

  constructor(protected formBuilder: FormBuilder) {}

  ngOnInit() {
    this.surveyForm = this.formBuilder.group({
      Survey: new FormGroup({
        Id: new FormControl("", Validators.required),
        Name: new FormControl("", Validators.required)
      }),
      QuestionAnswerSets: new FormArray([])
    });
  }

  initSet() {
    return this.formBuilder.group({
      Question: new FormControl("", Validators.required),
      Answers: new FormArray([])
    });
  }

  addSet() {
    const questionAnswerSets = <FormArray>(
      this.surveyForm.controls.QuestionAnswerSets
    );
    questionAnswerSets.push(this.initSet());
  }

  removeSet(setId: number) {
    let questionAnswerSets = <FormArray>(
      this.surveyForm.controls.QuestionAnswerSets
    );
    questionAnswerSets.removeAt(setId);
  }

  initAnswer() {
    return this.formBuilder.group({
      Answer: new FormControl("", Validators.required)
    });
  }

  addAnswer(setId: number) {
    const questionAnswerSets = <FormArray>(
      this.surveyForm.controls.QuestionAnswerSets
    );
    const answer = <FormArray>(
      questionAnswerSets.controls[setId]["controls"].Answers
    );
    answer.push(this.initAnswer());
  }

  removeAnswer(setId: number, answerId: number) {
    const questionAnswerSets = <FormArray>(
      this.surveyForm.controls.QuestionAnswerSets
    );
    const answer = <FormArray>(
      questionAnswerSets.controls[setId]["controls"].Answers
    );
    answer.removeAt(answerId);
  }
}

teamplate

<form [formGroup]="surveyForm" (ngSubmit)="saveForm(surveyForm.value)">
    <div formArrayName="QuestionAnswerSets">
        <fieldset *ngFor="let questionAnswerSet of surveyForm.controls.QuestionAnswerSets.controls; index as setId;" [formGroupName]="setId">
            <input type="text" formControlName="Question" />
            <div formArrayName="Answers">
                <div class="ui-g-12" *ngFor="let answer of this.surveyForm.controls.QuestionAnswerSets.controls[setId].controls.Answers.controls; index as answerId;"
                    [formGroupName]="answerId">
                    <input type="text" formControlName="Answer" />
                    <button type="button" (click)="removeAnswer(setId, answerId)">Remove Answer</button>
                </div>
            </div>
            <button type="button" (click)="addAnswer(setId)">Add Answer</button>
        </fieldset>
    </div>
    <button type="button" (click)="addSet()">Add Set</button>
</form>

Probably I've missed something, but it will give you an idea

like image 108
Timothy Avatar answered Oct 19 '22 00:10

Timothy


After reading so many articles on nested models template driven forms and official angular documentation i could say this can not be done using template driven forms as there no directive which represents an array. (grouping as how fieldset does for an object with ngModelGroup)

This could be done with angular reactive forms with FormArrayName

https://angular.io/api/forms/FormArrayName

like image 25
Mahesh Avatar answered Oct 19 '22 00:10

Mahesh