Angular binding object with array from component to view with ngModels

Jérémie Chazelle picture Jérémie Chazelle · Aug 30, 2017 · Viewed 8.7k times · Source

I tried to bind my model on my view, but I have a problem when I submit my form : I don't have an array, but many property.

component :

export class QuizFormAddQuestionComponent implements OnInit {
    public question: Question;

    constructor() {
        this.question = new Question();
    }

    ngOnInit() {
        this.question.setModel({ question: 'test' });
        this.question.setAnswers(3);
    }

    createQuestion(form) {
        console.log(form.value);
    }

}

my template :

<hello name="{{ name }}"></hello>

<div class="row">
    <div class="col-sm-1"></div>
    <div class="col-sm-10">

        <form #form="ngForm" (ngSubmit)="createQuestion(form)" class="mt-4">
            <div class="form-row">
                <div class="form-group col-md-12">
                    <label for="question" class="col-form-label">Question</label>
                    <input type="text"
                           class="form-control"
                           id="question"
                           placeholder="Enter your question..."
                           name="question"
                           [ngModel]="question.question"
                           required>
                </div>
            </div>
            <div class="form-group row" *ngFor="let answer of question.answers; let i = index;">
                <div class="col-sm-12">
                    <div class="form-check">
                        <label class="form-check-label">
                            <input class="form-check-input"
                                   type="checkbox"
                                   id="answer-value-{{i}}"
                                   [ngModel]="question.answers[i].value"
                                   name="answers[{{i}}].value">
                            Answer {{ i }}
                        </label>
                    </div>
                    <input type="text"
                           class="form-control"
                           id="answer-text-{{i}}"
                           [ngModel]=question.answers[i].text
                           name="answers[{{i}}].text">
                           {{ answer.getManipulateObjet() }}
                </div>
            </div>
            <div class="form-row">
                <div class="form-froup col-md-12 text-right">
                    <button type="submit" class="btn btn-primary">Add question</button>
                </div>
            </div>
        </form>
    </div>
</div>

question.ts (model)

import { Answer } from "./answer";

export class Question {

    constructor(private question: string          = '',
                private answers: any[]            = [],
                private more_informations: string = '',
                private difficulty: number        = 0,) {
    }

    setModel(obj) {
        Object.assign(this, obj);
    }

    addAnswer() {
        let new_answer = new Answer();

        new_answer.setModel({ text: 'test', value: false });

        this.answers.push(new_answer);
    }

    setAnswers(number_answers) {
        for (let i = 0; i < number_answers; i++) {
            this.addAnswer();
        }

        console.log(this);
    }

}

answer.ts (model)

export class Answer {

    constructor(private text: string  = '',
                private value: boolean = false,) {
    }

    setModel(obj) {
        Object.assign(this, obj);
    }

    getManipulateObjet() {
      return '=== call getManipulateObjet() for example : return more information after manipulating object, count value properties, concat, etc... ===';
    }

    getCount() {
      // ....
    }

    getInspectMyObject() {
      // ...
    }
}

initial object :

enter image description here

console after submit :

enter image description here

After the submit I would like the same things as the initial object (same structure with the data updated), the same data structure before and after submit

I tried this on in my view, but it doesn't work :

<div class="form-group row" *ngFor="let answer of question.answers; let i = index;">
    <div class="col-sm-12">
        <div class="form-check">
            <label class="form-check-label">
                <input class="form-check-input"
                       type="checkbox"
                       id="answer-value-{{i}}"
                       [ngModel]="answer.value"
                       name="answer[{{i}}].value">
                Answer {{ i }}
            </label>
        </div>
        <input type="text"
               class="form-control"
               id="answer-text-{{i}}"
               [ngModel]=answer.text
               name="answer[{{i}}].text">
               {{ answer.getManipulateObjet() }}
    </div>
</div>

I transform in the *ngFor [ngModel]=question.answers[i].text by [ngModel]="answer.text", but I have the same problem...

I tried many things from differents posts : Angular 2 form with array of object inputs, Angular 2 - 2 Way Binding with NgModel in NgFor

But always many properties and no array

I would like to do this without reactive form, only template driven

Demo:

https://angular-by7uv1.stackblitz.io/

I would like to use different function from my object 'answer', for example : answer.getInformation(), getCount(), getInspectMyObject() etc.. to iterate on my object only, in my view. This function is provided by my model 'Answer' to have a clean code in my view. I would like use many functions from my model in the *ngFor. If I use the reactive form, I can't use different function from my model because the "link" between my parent object and my childs is broken.

SOLVED

https://fom-by-template-driven.stackblitz.io

Answer

Rajez picture Rajez · Sep 9, 2017

The problem is, whatever the input name specified, it will be considered as just text. Should not reference any Object or Array.But with slight modifications, there is a chance to obtain the same data structure with updated data.

All elements are binded in one way, so make it two way binding. So whenever input value changes, The binded element value updates.

 [(ngModel)]="question.question"

Use template driven form for validation

<form #form="ngForm" (ngSubmit)="createQuestion()" class="mt-4">
...
<button [disabled]="!form.valid"  

Now the values are validated & updated in the variable question.There is no need to get the value from form, use the updated object question to create question.

createQuestion() {
        console.log(this.question);
    }

View the results in worked out App