Angular2: How do you properly bind to nested data?

3.8k Views Asked by At

I have a component I'm going to use as a shell for multiple choice questions to load into. Here's how the component is set up so far

component

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

export class Answers{
    id: string;
    answer: string;
}

const answers: Answers[] = [
        {
        id: 'exp01q',
        answer: 'Its fine as is.'
        },
        {
        id: 'exp02q',
        answer: 'I want to make minor adjustments.'
        },
        {
        id: 'exp03q',
        answer: 'I want to change my image'
        },
        {
        id: 'exp04q',
        answer: 'Ive never wanted to use a particular image until now.'
        }
    ];

@Component({
    moduleId: module.id,
    selector: 'multi-radio-btn',
    templateUrl: 'multi-rad-btn.component.html',
    styleUrls: ['multi-rad-btn.component.css']
})

export class MultiRadioBtnShell {

    question = 'How do you feel about your current image?';
    id = 'exp-img-q';
    name = 'exp-ques1';
    ans = answers;

}

HTML Template

<h3>radio button shell</h3>

<div class="row justify-content-center">
    <fieldset [attr.id]='id' class="card col-8 justify-content-center">

        <label class="ques-title">
            {{question}}
        </label>

        <div class="row answer-row-section justify-content-center">

            <div *ngFor="let answers of ans" class="col col-12 answer-row justify-content-center">
                <div class="col justify-content-center">

                    <input type="radio"
                        [attr.id]="answers.id"
                        [attr.name]="name"
                        [attr.value]="answers.answer" hidden />

                    <label [attr.for]="answers.id" class="col ques-ans-title" style="background-color: #4b73a0;">
                        {{answers.answer}}
                    </label>
                </div>
            </div>

        </div>

    </fieldset>
</div>

The reason it's set up like this now is because the way I was trying to do it at first wasn't working so I went to the Tour of Heroes tutorial to follow along with how they loaded all the heroes. The problem was coming from answer not being defined. So I rigged that part up the same way they did the heroes just for the sake of doing something I'm able to follow just to be sure I get the mechanics of how things load.

The original way I tried to do it was with this

// I had this right above the component
export class ExpQ{
    question: string;
    id: string;
    name: string;
    answers:[
        {
        id: string;
        answer: string;
        }
    ]
}

// I had this in the component's class
export const expq: ExpQ[] = [
    {
    question: 'How do you feel about your current image?',
    id: 'exp-img-q',
    name: 'exp-ques1',
    answers:[
        {
        id: 'exp01q',
        answer: 'Its fine as is.'
        },
        {
        id: 'exp02q',
        answer: 'I want to make minor adjustments.'
        },
        {
        id: 'exp03q',
        answer: 'I want to change my image'
        },
        {
        id: 'exp04q',
        answer: 'Ive never wanted to use a particular image until now.'
        }
        ]
    }
]

I was calling it in the html with

{{expq.question}}, {{expq.name}}, {{expq.answers.id}}, {{expq.answers.answer}}, etc.

at first with just loading the question it worked fine, but as I got to the answers: part it started breaking. I came across this https://scotch.io/tutorials/using-angular-2s-model-driven-forms-with-formgroup-and-formcontrol and seen the syntax for the addresses: part was pretty much the same as how I needed to structure my data. So I remade everything to resemble that. I still had no luck getting it to work.

Ultimately I'm going to be sending the questions through the parent component with @input and @output as well as a couple other tricks I came across. But before I can even think about that I need to get a handle on how to put the data all into one source so that it properly reads the nested bits of data. All the examples I come across are simple single tier bits of data, so I'm not sure on the syntax I need to use. How can I make this work?

2

There are 2 best solutions below

4
On BEST ANSWER

You can define your model like so:

export interface Answer {
  id: string;
  answer: string;
}

export interface Question {
  id: string;
  name: string;
  question: string;
  answers: Answer[];
}

Then your component could have this to test

question1: Question = {
  id: 'q1',
  name: 'q1',
  question: 'Does TypeScript rule?',
  answers: [
    { id: 'a1', answer: 'Yes' },
    { id: 'a2', answer: 'Of Course' },
    { id: 'a3', answer: 'Duh' }
  ]
};

Of course the names don't have to be the same but I think this gives you a better idea of how to model nested data.

Then to display it you will need to iterate over nested structures. Look up the *ngFor directive. You will want to iterate over your answers in this case. Ex:

<div *ngFor="let answer of question1.answers">
  {{answer.id}} - {{answer.answer}}
</div>
0
On

Need to flatten the objects,

Params :

Objects : at least n>0 array off JSON objects (dose not matter is circular) target : {}

path : ""

Note : make sure the Objects Array passed in is n>0 at least

flatten(objects, target, path) {
let me = this;    
let retArray = [];
for(let x=0; x < objects.length; x++) {

  let object = objects[x];
  path = path || '';
  target={};

  target = me.flattenHelper(object, target, path);

  retArray.push(target);
}
return retArray;}

..

flattenHelper(object, target, path){
let me = this;
Object.keys(object).forEach(function (key) {
  console.log("key : "+ key + " : object : " +  (object[key] && typeof object[key] === 'object') + " path : " + path);
  if (object[key] && typeof object[key] === 'object') {
      me.flattenHelper(object[key], target, path + key);
  }
  target[path + key] = object[key];
  console.log(target);
});
return target;}