How to use ngFor with ngModel


#1

I’m trying to get the value of a checkbox I generated with a * ngFor with firebase.

I get te value with [(ngModal)]=“var”, but I can’t put it question.question because that variable has to be created in the .ts(Angular)

How to use ngFor with ngModel to get the value of each element that is created in the DOM

My view:

<div class="Pregunta" *ngFor="let pregunta of preguntas | async">
        <ion-row class="PreguntaTitulo" >
          <ion-col coll-12>{{pregunta.pregunta}}</ion-col>
        </ion-row>
        <ion-list radio-group [(ngModel)]='pregunta.pregunta' >
        <ion-row class="Respuesta"> 
          <ion-col col-4 col-sm> 
            <ion-item>
              <ion-label color='secondary'>Bueno</ion-label>
              <ion-radio [value]="bueno" color='secondary'></ion-radio>      
            </ion-item>
          </ion-col>
          <ion-col col-4 col-sm> 
            <ion-item>
              <ion-label color='amarillo'>Regular</ion-label>
              <ion-radio [value]="regular" color='amarillo'></ion-radio>      
            </ion-item>
          </ion-col>
          <ion-col col-4 col-sm> 
            <ion-item>
              <ion-label color='danger'>Malo</ion-label>
              <ion-radio [value]="malo" color="danger"></ion-radio>      
            </ion-item>
          </ion-col>
        </ion-row>
        </ion-list>
</div>

My ts:

import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { AngularFireDatabase, FirebaseListObservable } from 'angularfire2/database';
@Component({
  selector: 'page-checklist',
  templateUrl: 'checklist.html',
})
export class ChecklistPage {
 
 //if i creat a element with [(ngModel)]= 'var', I create that variable here,  example( var:any ; )
 // and I can use it in any method
 //How creat the var for ngModel

  preguntas: FirebaseListObservable <any>;

  constructor(public navCtrl: NavController, 
    public navParams: NavParams,
    public afDB: AngularFireDatabase) {
      this.preguntas = afDB.list ("/vehiculo");
    }

  ionViewDidLoad() {}

}

#2

I’ve never used Firebase, so can’t probably speak to the fundamental issue, but you should not have listened to @gauz09’s advice in your previous thread.


#3

@rapropos - would you be able to provide the right approach to do this then?

It would help me too.

Thanks!


#4

What @RaulAndrade had initially, with value="foo", is what (I think) was desired: to set the value to the literal string "foo". What you suggested ([value]="foo") sets the value to the controller object property named foo.


#5

I have done this successfully with both checkboxes and radio buttons. But I don’t understand your question, because I see no checkboxes in your code. I haven’t looked at the previous thread because I don’t want to deal with history or drama. Could you tell me, in a sentence or two, exactly what you want to do?


#6

NG For and NgModel means your have existing values in the controller, friend. If all fails, it simply means you don’t have valus in the page to start with, then, to push on the button.

Hope it helps,

Francois, digico FR


#7

I have to put a questionnaire of 28 questions only with 3 answer possible (Good regular and bad), but I do not want to write the questions one by one, So I want to get them from a database and link the answer to the question and if I wanted to add more questions I would just add the question to the database

So I want to use ngfor to show the user, but I do not know how to retrieve the answer for each question


#8

It sounds as though you don’t need a reactive form. (A form is reactive if, for example, the choices presented to the user change if “United States” or “Canada” is selected. An input field might change from “State” to “Province.”)

I would recommend this approach:

  1. Initialize your Form variable to some dummy value so the template doesn’t complain.
  2. In your ts file, maybe inside IonViewDidLoad(), read the data from the database, and construct an array of questions.
  3. Simultaneously as you build your array, build a Form using FormBuilder.
  4. In your template, use ngFor to display each question in the array, and associate each radio button to the respecting formControl (or formControlName) in the Form you built.
  5. Remember to call (ionChange) if you are using radio buttons, and inside (ionChange) to update Angular change detection, so the Angular Form stays consistent with the inputs Ionic is receiving.

That approach works, and it gives you a lot of control. You can include a lot of different kinds of inputs (configured at runtime) in the same Form if you build the Form along with the inputs.


#9

While there’s absolutely nothing wrong with @AaronSterling’s suggestion, it’s not the only way to go about this. If you can somehow get the firebase data into an ordinary array of questions, it is perfectly viable to do what you are doing right now with binding the radio group to a property of the pregunta loop variable.


#10
questionArrayStream: Observable<string[]>;

this.questionArrayStream = this.angularFire.list(referencePointInDatabase); // returns every question at once, assuming the list of questions is a list of strings

One way I solved the OP’s type of issue, when I didn’t know ahead of time how many “questions” there were going to be, was by constructing a Map<integer, boolean> if the answer to the questions were either true or false. Then using the ngFor let i of index to map i to either true or false depending on which radio button was chosen. Then back in the ts file, once the user submits the form, you know after the fact how many questions there were by measuring the length of the array emitted by the Observable. (I just don’t think this is a powerful approach, so I’ve stopped using it.)


#11

I have a very similar situation, except my questions can be of various types, such as multiple choice / free text / record audio, so my Question looks like this:

export interface Question {
  style: "choose" | "write" | "speak";
  prompt: string;
  choices?: string[];
  multi?: boolean;
  answer?: string;
}

In the template, I merely bind to the answer property of each question (showing only the multiple choice option because it’s closest to OP’s situation):

<ion-item *ngFor="let q of questionnaire">
  <ion-select *ngIf="q.style === 'choose'"
      [multiple]="q.multi" [(ngModel)]="q.answer">
    <ion-option *ngFor="let choice of q.choices" [value]="choice">{{choice}}</ion-option>
  </ion-select>
</ion-item>

Display html element from JSON object on ionic App
#12

Maybe this is too “theoretical,” but it seems to me that structure knows something it shouldn’t. I guess I’d be more comfortable with that approach if Question was just a string, and QuestionAndAnswer extended Question with the ability to include answers. Then you get the Observable stream of Questions from the database, and the form when submitted produces an array of QuestionAndAnswer.


#13

Regular readers of this column are probably familiar with my disdain for inheritance as a solution in search of a problem.

A single string doesn’t convey enough information to cover all the different types of questions. The array of Questions (with no answers) comes from the server, and the app returns the filled-out version. This way, the filled-out questionnaire is a standalone object, saving me the headache of synchronizing versions of questionnaires in a situation like this:

  • client A fills out questionnaire
  • admin adds a new question, deletes one, and switches the type of another
  • client B fills out questionnaire

A system that passes back only naked answers without context would be much more complicated to manage on the backend, and since the questionnaires rarely get particularly long, I’m not overly concerned about bandwidth or storage space.