6

In my TS file I am dynamically creating the properties on my selectedValsObj object like this:

private selectValsObj: any = {};

setSelectedValsObj(sectionsArr) {
  sectionsArr.forEach(section => {
    section.questions.forEach(questionObj => {
      if (questionObj.type === 'drop-down') {
        this.selectValsObj[questionObj.questionId] = { selected: questionObj.answerDetails[0] };
      }
    })
  });
}

In my HTML I want to bind the [ngModel] on my inputs to the properties on the selectValsObj object. I have tried this but have had no luck:

<div *ngFor="let question of section.questions">
    <div class="drop-down-question" *ngIf="question?.type === 'drop-down'">
        <select class="q-select"
                [(ngModel)]="selectValsObj[questionId].selected" // <== doesnt work either**
                // [(ngModel)]="selectValsObj[{{ questionId }}].selected" // <== doesnt work**
                name="answerForQuestion{{ question?.questionId }}">
            <option *ngFor="let answer of question?.answerDetails"
                [ngValue]="answer">
                    {{ answer?.value }}
            </option>
        </select>
    </div>
</div>

How can I set the ngModel in my HTML to a dynamically created property in my TS file?

georgej
  • 3,041
  • 6
  • 24
  • 46

4 Answers4

5

I tried to replicate the situation but in the code, you have posted seem to be multiple problems.

  1. property selectValsObj is declared as private but you are trying to use it in the template
  2. in the template you are trying to iterate over section.questions but I don't see it defined anywhere else but in the setSelectedValsObj method forEach local scope
  3. You might be using your data wrong because of lacking type definitions

This is your code as I understood it and added typedefs

interface QuestionModel {
  type: string;
  questionId: string;
  answerDetails: string[];
}

const MOCK_DATA = [
  {
    questions: [{
      type: 'drop-down',
      questionId: '42',
      answerDetails: ['wololo'],
    }],
  },
];


@Component(...)
export class ProductsComponent {
  selectValsObj: { [key: string]: { selected: string } } = {};

  constructor() {
    this.setSelectedValsObj(MOCK_DATA);
  }

  setSelectedValsObj(sectionsArr: { questions: QuestionModel[] }[]) {
    sectionsArr.forEach(section => {
      section.questions.forEach(questionObj => {
        if (questionObj.type === 'drop-down') {
          this.selectValsObj[questionObj.questionId] = {selected: questionObj.answerDetails[0]};
        }
      });
    });
  }
}

After you check if the type definitions are as you originally intended (and use it properly) you will prevent a lot of bugs.

Also, consider using more declarative approach, map and filter your data instead of using forEach in a method.

Vojtech
  • 2,756
  • 2
  • 19
  • 29
1

For the following line in your HTML code:

[(ngModel)]="selectValsObj[questionId].selected"

If you didn't define the questionId as a variable that has a value anywhere in your ts file, then that can be the problem.

If you want to get questionId of each question in your section.questions list that you are looping on you can try to make it as follows:

[(ngModel)]="selectValsObj[question.questionId].selected"

I have made a simple example, you can find it here (in the src/app.ts) with different working cases. I hope it can help.

Hend Khalifa
  • 647
  • 6
  • 10
1
(click)="toggle(address.id)"
  *ngIf="action[address.id]"

in ts file:

action: any = {};

in toggle method,

this.action[addressId] = !this.action[addressId];
Vineeth Sai
  • 3,389
  • 7
  • 23
  • 34
Margi
  • 11
  • 1
0
  • It is not very clear what you are trying to achieve.
  • I assume you are trying to dynamically build a page with questions and answers in combo-box if it is of type 'drop-down'.
  • You are also trying to capture the selected answer.

if so... below code snippet can help.

import { Component, OnInit } from '@angular/core';

interface Question {
  questionStr: string;
  questionId: string;
  type: string;
  answers: Array<string>;
  selectedAnswer: string;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'app';

  selectValsArr: Array<Question> = [];

  ngOnInit() {
    this.setSelectedValsObj([
      {
         questionStr : 'Question1',
         questionId: 'Q01',
         type: 'drop-down',
         answers: ['Q1Ans1', 'Q1Ans2', 'Q1Ans3'],
         selectedAnswer: null
      },
      {
        questionStr: 'Question2',
        questionId: 'Q02',
        type: 'drop-down',
        answers: ['Q2Ans1', 'Q2Ans2', 'Q2Ans3'],
        selectedAnswer: null
      },
    ]);
  }


  setSelectedValsObj(sectionsArr: Array<Question>) {
    sectionsArr.forEach(section => {
      if (section.type === 'drop-down') {
        this.selectValsArr.push(section);
        }
    });
  }
}

and HTML

<div *ngFor="let question of selectValsArr">
  <h3>{{question.questionStr}}</h3>
  <div class="drop-down-question">
    <select [(ngModel)]="question.selectedAnswer">
      <option value="">Select an Answer</option>
      <option *ngFor="let ans of question.answers" [ngValue]="ans"> {{ans}}</option>
    </select>
  </div>
</div>

<br>
<h2>Selected Answer</h2>

<div *ngFor="let question of selectValsArr">
    <span>{{question.questionStr}}</span>
    <span>&nbsp; &nbsp; - &nbsp; {{question.selectedAnswer}}</span>
</div>