Angular binding object with array from component to view with ngModels
Asked Answered
H

5

8

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

Halfway answered 30/8, 2017 at 20:16 Comment(17)
from your template, add code for your Add Question buttonParmentier
@Faisal I updated my postBellinger
im not entirely sure what the problem is but if you arent getting data, you need to add a standalone property to each inputCreepy
@Creepy After the submit I would like the same things as the initial object (same structure with the data updated)Bellinger
you mean you want the data to reset? or you want the same data structure?Creepy
@Creepy the same data structureBellinger
not too sure as i dont use forms like that, i read the data myself so i know i will always have the same structureCreepy
what is the problem you are facing can you explain a bit , the data is coming as objects , and you want it as array of objects on submit is that so ?Perspicacity
@RahulSingh 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 submitBellinger
same data structure means , you have a form it will follow the same structure @JérémieChazellePerspicacity
@RahulSingh This is the problem. I can not recreate the same structure with my formBellinger
@JérémieChazelle can you provide the stackblitz code base link ?Perspicacity
@RahulSingh sure stackblitz.io/edit/angular-by7uv1Bellinger
@JérémieChazelle how is the structure you are looking at can you just give a small gist on the structurePerspicacity
Let us continue this discussion in chat.Perspicacity
@JérémieChazelle still need help in this?Phellem
@Phellem it's ok thx !!Bellinger
P
3

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

Phellem answered 9/9, 2017 at 17:27 Comment(0)
A
0

You can do something like this:-

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

import {FormGroup, FormArray, FormControl} from '@angular/forms';

@Component({

  selector: 'form-component',

  template: `

    <form [formGroup]="formGroup">
      <div formArrayName="inputs" *ngFor="let control of formArray;     let i = index;">
        <input placheholder="Name please" formControlName="{{i}}"/>
      </div>
    </form>

    <button (click)="addInput()">Add input</button>

  `
})

export class FormComponent implements OnInit{

  formGroup: FormGroup;


  contructor() {
  }


  ngOnInit(){

    this.formGroup = new FormGroup({
      inputs: new FormArray([
        new FormControl("Hello"),
        new FormControl("World"),
      ]);
    });
  }

  get formArray() {

    return this.formGroup.get('inputs').controls
  }


  addInput(){

    this.formArray.push(new FormControl("New Input"));
  }

}
Anisotropic answered 31/8, 2017 at 18:33 Comment(0)
N
0

i see that you are using [ngModel] in you html template. this will only bind the input to bind the output as well you need to use [(ngModel)]. this is the regular way of using ngModel. i find one more wrong in your code that name="answer[{{i}}].value"here name is value should be constant, you are giving it as variable the correct way of writing it is name="{{answer[i].value}}"

enter image description here

Nicotine answered 5/9, 2017 at 7:21 Comment(10)
When I modified with your modifications I have many errors, If you want to try it : stackblitz.com/edit/angular-by7uv1Bellinger
hi i modified the code what i said in the answer it worked fine this is the code i added in the html.Nicotine
i am not able add the html code here it says to long. How can i share the code to youNicotine
You can Fork my project on stackbiltz and share the link in your postBellinger
Sure : ERROR TypeError: Cannot read property '0' of undefined at Object.eval [as updateDirectives] (AppComponent.html:31) at Object.debugUpdateDirectives [as updateDirectives] (Bellinger
just a try make this change in the question model addAnswer() { this.answers.push(new Answer()); this.answers['text']='test'; this.answers['value']=false; }Nicotine
please change in you project on stackblitz and share it or updated your postBellinger
Same error, may be save your project to apply your modification ?Bellinger
angular-dhuwaf.stackblitz.io. i have saved this. and working fineNicotine
open your console on stackblitz.com/edit/angular-dhuwaf, many errorsBellinger
P
0

The following scenario is not possible using the current Angular Template driven form. There is a issue raised to the Angular Team LINK.

Its requires a lot of rework as the template driven forms uses ngModel . Angular Forms are moving towards reactive approach and in the near feature i dont see this feature being added to it.

Please use Reactive forms for this formArrayName will be sufficient to help get the desired result.

Perspicacity answered 6/9, 2017 at 10:4 Comment(0)
F
0
academicYearTerms: [{
    term_name: {
        type: String,
        default: " "
    },
    term_end: {
        type: Date,
    },
    term_start: {
        type: Date,
    },
}]

declare in node js

adminpageform = new FormGroup({
    start_date: new FormControl(''),
    end_date: new FormControl(''),
    acadyr_shortcode: new FormControl(''),
    no_terms: new FormControl(''),
    term_name: new FormControl(''),
    term_end: new FormControl(''),
    term_start: new FormControl(''),
    TermsId: new FormControl('')
});

declare in ts how to store in array of object value in form control

Flaming answered 13/12, 2019 at 6:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.