0

I'm developing a feedback form in Angular using reactive forms approach. The response from the server has questions array which in turn has answers array.

Here is the response.

value": [
{
"description": "why didn't you turn up"
"answers": [
{
"description": "Unexpected Personal Committment"
},
{
"description": "Unexpected Official Work"
},
{
"description": "Even Not What I Expected"
},
{
"description": "Did Not Receive Further Information About The Event"
},
{
"description": "Incorrectly Registered"
},
{
"description": "Do Not Wish to Disclose"
}
]
}
]

I'm trying to build reactive form as follows.

ngOnInit() {
    this.feedbackForm = this.fb.group({
      questions: this.initQuestion()
    })                     
  }

initQuestion() {
    return this.fb.group({
      description: [],
      answers: this.initAnswer()
    })
  }

  initAnswer() {
    return this.fb.group({
      description: this.fb.array([])
    })
  }

How to map the question and response to my reactive form.

I tried as follows, but never works,

get questions(): FormArray {
    return <FormArray>this.feedbackForm.get('questions');
  }

  get answers(): FormArray {
    return <FormArray>this.feedbackForm.get('questions').get('answers');
  }

displayQuestions(questions: Response<Question[]>): void {
    if (this.feedbackForm) {
      this.feedbackForm.reset();
    }
    this.questionsResponse = questions;

   if (this.questionsResponse.value.length > 0) {
      this.feedbackForm.setControl('questions', this.fb.array(this.questionsResponse.value || []));
      this.feedbackForm.setControl('answers', this.fb.array(this.questionsResponse.value.answers || []));
    }           
  }

Here is my template

<form [formGroup]="feedbackForm" novalidate (ngSubmit)="send()"> 
<div formArray="questions">
  <div [formGroup]="i" *ngFor="let question of feedbackForm.controls.questions.controls; let i=index;">
    <p>{{question.description}}</p>
    <div formArray="answers">
      <div [formGroup]="j" *ngFor="let answer of question.controls.answers.controls; let j=index">
      <label [attr.for]="j">{{answer.description}}</label>
      <input type="radio" [id]="j" [formControlName]="j" value="{{answer.descripiton}}" />
    </div>
    </div>
  </div>
</div>

<input type="submit" value="Send">

I'm trying to display question with answers and I need user to select answer for the question and on submit I need the selected answer values.

2
  • Post your template. Also you could make a StackBlitz example Commented Mar 7, 2019 at 10:54
  • @ritaj : Here is the StackBlitz of my scenario Commented Mar 7, 2019 at 14:38

2 Answers 2

1

Abdul you has two differents things:

  1. A "structure to make questions"
  2. A form

but you are not defined your form. For this, you need know how is the data you want to send to the server. I must supouse you want send to the server some like

[1,3,1]
//or
[{question:1,answer:1},{question:2,answer:3},{question:3,answer:1}]
//or
[
   {question:'why didn't you turn up',answer:'Unexpected Official Work'},
   {question:'why didn't you do down',answer:'I don't know'}
]

As you can see, anyway you only need a unique formArray, you use the response of server to create the view of the form, but not the formArray

What do you want to send to the server?

Well, if we choose the third option is an FormArray, not a formGroup. Don't take care about it. A FormArray can be mannage like a FormGroup. First we are going to see how create the form Array

 ngOnInit() {
    this.feedbackForm = new FormArray(this.data.map(x=>new FormGroup({
      'question':new FormControl(x.description),
      'answer':new FormControl('')
    })))
  }

See that feedbackform is a Form Array. How create it? with each question, create a formGroup with two Form controls, question and answer

The .html becomes like

<form *ngIf="feedbackForm" [formGroup]="feedbackForm" (submit)="send()">
      <div *ngFor="let control of feedbackForm.controls;let i=index"   >
        <div [formGroup]="control">
          <input formControlName="question">
          <div *ngFor="let a of data[i].answers">
            <label>
              <input type="radio" [value]="a.description" formControlName="answer">
              <span>{{a.description}}</span>
            </label>
          </div>
        </div>

        </div>
      <button type="submit">submit</button>
    </form>

<pre> {{feedbackForm?.value | json}} </pre>

See that, we use the "data" to show the label of the radios and to give value to the radios, but the Form is only a form array. If our "data" have an "id" to the question and to the answer, we could use it

See the stackblitz

NOTE: I just add in the stackblitz a typical FormGroup with a FormArray because it's different the .html

Sign up to request clarification or add additional context in comments.

5 Comments

If you look at the template in my question, I need to send the selected answers to the server. But I'm struck with my form template. It's show error while reading controls. Please help
can you put an example of the data you want to send (in format json)? else you're putting the cart before the horse
it's same as what you have mentioned in your answer. the second option in your reply. array of objects containing question and selected answer
@Abdult, I updated my answer. but the important is that we need know what we wnat to send to the server, after we create a FormGroup (or a FormArray). At firs only wirete in the .html {{myform?.value|json}} then we write carefully the html tags taking account that is a formArray, we use formGroup to refered to it at first, formArray.controls to controls. If our controls is a FormGroup make a div to say Angular is a formgroup, etc.
Thanks for the great explanation. You really deserve a Salute.
1

You were doing a bit 'upsidedown'. The right way would be to go trough every member of the array and create the control for it, go to the nested arrays, create the controls for them aswel and then push it to the questions array.

SetControl replaces only an existing control. https://angular.io/api/forms/FormArray#setcontrol .

Replace the initial definition with this one.

  feedbackForm:FormGroup = this._fb.group({
    questions:this._fb.array([])
  })

And try replacing that last if with this one.

if (this.questionsResponse.value.length > 0) {
    this. questionsResponse.forEach(question => {
    (this.feedbackForm.get('questions') as FormArray).push(this.fb.group({
      description:question.description,
      answers:this.fb.array([...question.answers.map(answer => 
         this.fb.group(answer))])
     }))
    })
  }

EDIT: Here is the required template for the code above to display.

<form [formGroup]="feedbackForm" novalidate (ngSubmit)="send()">
    <div formArrayName="questions">
        <div *ngFor="let question of feedbackForm.get('questions').controls; let i=index;">
            <p>{{feedbackForm.get(['questions',i,'description']).value}}</p>
            <div [formGroupName]="i">
        <div formArrayName="answers">
          <div *ngFor="let ans of feedbackForm.get(['questions',i,'answers']).controls; let j = index">
              <div [formArrayName]="j">
                <input formControlName="description" />
              </div>
          </div>
        </div>
            </div>
        </div>
    </div>

    <input type="submit" value="Send">
</form>

<pre> {{feedbackForm.value | json}} </pre>

3 Comments

@Qeilson Thanks for your help and response. I have changed as per your answer, but I'm getting error in my template. Here is the StackBlitz of my scenario. I have also updated the question to reflect my template.
@Abdul ive edited the aswer with the required template :)
Thanks for the template

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.