5

How to manage with Angular2 a form who hold an undefined number of field ?

In my case, I need to create a from where user can add and delete some block of fileds. It's like an address book where user can add one or ten address. And each address had a some fields like street, street number and so on.

My look like this :

let address = fb.group({
        street: fb.control(null, Validators.required),
        streetNumber fb.control(null, Validators.required)
    });

this.userForm = fb.group({
        name: fb.control(null, Validators.required),
        firstName: fb.control(null, Validators.required),
        address: fb.group({
            1: address
            })
        });

I really don't know how to manage this in the template.

I've try to write some thing like that in the template, but obviously, it doesn't work...

<form [ngFormModel]="userForm">
<input type="text" ngControl="name" #name="ngForm"/>
<input type="text" ngControl="firstName" #firstName="ngForm"/>

<div *ngFor="#address of userForm.controls['address'].controls">
    <input type="text" ngControl="street" #street="ngForm"/>
    <input type="text" ngControl="streetNumber" #streetNumber="ngForm"/>
</div>

EDIT

I've made a Plunker for a better explanation http://plnkr.co/edit/ffYe1479WnxYOQrbxwLF?p=preview

Waldo
  • 1,070
  • 1
  • 11
  • 30
  • What does "obviously doesn't work mean"? – Günter Zöchbauer Apr 14 '16 at 15:29
  • The template I've wrote don't do the job, because it's not the right way to write it. Angular raise error because he don't find the control 'street' in the control group 'userForm'. – Waldo Apr 14 '16 at 15:33
  • Doesn't look like you need `#street="ngForm"`. What if you remove it? – Günter Zöchbauer Apr 14 '16 at 15:35
  • #street="ngForm" is useful for me because I use it in an component to show some errors to the user. I think the issue come from ngControl="street", because 'street' isn't defined in 'userForm' control, but in the sub control group 'address' – Waldo Apr 14 '16 at 15:38
  • 1
    see [this almost similar question](http://stackoverflow.com/questions/35652576/ngcontrol-with-ngfor-in-angular2), is this what you want ? – Ankit Singh Apr 14 '16 at 17:00
  • @A_Singh, Yes it's something like that – Waldo Apr 15 '16 at 06:32
  • than, you just need to do what [Günter suggested](http://stackoverflow.com/a/35652723/5612697). Apart from `array of controls` you can also use `addControl` and `removeControl` on `controlGroup` if you like. – Ankit Singh Apr 15 '16 at 06:36
  • I try, it didn't work. You can take a look at http://plnkr.co/edit/ffYe1479WnxYOQrbxwLF?p=preview – Waldo Apr 15 '16 at 06:40

1 Answers1

6

I implemented it for you, see Plunker or better yet, Plunker -@waldo

import {Component} from 'angular2/core';
import {
  FORM_DIRECTIVES, FormBuilder, ControlGroup, ControlArray, Validators, NgForm, Control,
  AbstractControl
} from 'angular2/common';

@Component({
  selector: 'my-app',
  template: `
    <form [ngFormModel]="userForm" *ngIf="userForm">
      <p><label><input id="date" type="text" ngControl="name" #name="ngForm"/> Name</label></p>

      <p><label><input id="date" type="text" ngControl="firstName" #firstName="ngForm"/> FirstName</label></p>

      <h3>Add address</h3>
        <ul ngControlGroup="addresses">

        <li *ngFor="#ctrl of objToArray(userForm.find('addresses').controls); #i = index" 
        ngControlGroup="{{ctrl}}">
           {{ctrl}}:
          <input ngControl="street" placeholder="Street">
          <input ngControl="streetNumber"  placeholder="StreetNumber"> 
        </li>
      </ul>

    <div (click)="addAddress()" style="cursor: pointer"> Add Another Address</div>

    </form>

    `,
  directives: [FORM_DIRECTIVES]
})
export class AppComponent {
  userForm: ControlGroup;

  constructor(private fb: FormBuilder) {

    this.userForm = fb.group({
      name: fb.control(null, Validators.required),
      firstName: fb.control(null, Validators.required),
      addresses: fb.group({
        address1: fb.group({
          street: fb.control(null, Validators.required),
          streetNumber: fb.control(null, Validators.required)
        })
      })
    });

    console.log(this.userForm);

  }

  objToArray(o){
    return Object.keys(o);
  }

  addAddress() {
    let addressField = this.fb.group({
      street: this.fb.control(null, Validators.required),
      streetNumber: this.fb.control(null, Validators.required)
    });

    (<ControlGroup>this.userForm.find('addresses')).addControl(
      'address' + (Object.keys((<ControlGroup>this.userForm.find('addresses')).controls).length + 1), addressField);
  }
}
Ankit Singh
  • 24,525
  • 11
  • 66
  • 89
  • 1
    Thanks! I've made some test by my side and finally find something like your code. http://plnkr.co/edit/ffYe1479WnxYOQrbxwLF?p=preview – Waldo Apr 15 '16 at 08:48
  • Hi, can you guys please help me with this problem http://stackoverflow.com/questions/38257443/onsubmit-return-all-the-rows-input-values-angular-2/38259063#38259063 – Gaurav Ram Jul 11 '16 at 21:22
  • Hi Waldo, when I try your method of adding another row, I get this error "[ts] Property 'push' does not exist on type 'AbstractControl'.". Any idea what I might be missing? – Gaurav Ram Jul 12 '16 at 19:01