Angular 2. How to use an array of objects for controls in reactive forms

I need to dynamically create textarea for forms. I have the following model:

 this.fields = { isRequired: true, type: { options: [ { label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' } ] } }; 

And the form:

 this.userForm = this.fb.group({ isRequired: [this.fields.isRequired, Validators.required], //... here a lot of other controls type: this.fb.group({ options: this.fb.array(this.fields.type.options), }) }); 

Part of the template:

 <div formGroupName="type"> <div formArrayName="options"> <div *ngFor="let option of userForm.controls.type.controls.options.controls; let i=index"> <textarea [formControlName]="i"></textarea> </div> </div> </div> 

So, as you can see, I have an array of objects, and I want to use the label property to show it in textarea . Now I see [object Object] . If I change options to a simple string array, for example: ['Option 1', 'Option 2'] , then everything works fine. But I need to use objects. So, instead of:

 <textarea [formControlName]="i"></textarea> 

I tried:

 <textarea [formControlName]="option[i].label"></textarea> 

But that does not work.
How can I use an array of objects?

This is plunkr

+5
source share
3 answers

You need to add a FormGroup that contains your label and value . It also means that the object created by the form has the same structure as your fields object.

 ngOnInit() { // build form this.userForm = this.fb.group({ type: this.fb.group({ options: this.fb.array([]) // create empty form array }) }); // patch the values from your object this.patch(); } 

After that, we correct the value using the method called in OnInit:

 patch() { // the formarray const control = <FormArray>this.userForm.controls.type.controls['options']; // iterate your object and pushes new values this.fields.type.options.forEach(x => { control.push(this.patchValues(x.label, x.value)) }) } // assign the values patchValues(label, value) { return this.fb.group({ label: [label], value: [value] }) } 

Finally here

Demo

+9
source

The answer from AJT_82 was so useful to me, I thought I would share how I used its code and built a similar example - one that may have a more common use case that invites several people to register immediately. Like this: form screenshot

I thought this might help others, so I am adding it here.

You can see that the form is a simple array of text inputs for email messages, with a custom validator being loaded on each of them. You can see the JSON structure in the screenshot - see the Preliminary line in the template (thanks to AJT), a very useful idea, developing to check if your model and controls are connected!

So first declare the objects we need. Note that 3 blank lines are model data (which we will associate with text inputs):

  public form: FormGroup; private control: FormArray; private emailsModel = { emails: ['','','']} // the model, ready to hold the emails private fb : FormBuilder; 

The constructor is clean (for easier testing, just order my userService to submit form data after submitting):

  constructor( private _userService: UserService, ) {} 

The form is built into the init method, including storing a reference to the emailsArray control, so we can check if its children (the actual inputs) are affected, and if so, whether they have errors:

  ngOnInit() { this.fb = new FormBuilder; this.form = this.fb.group({ emailsArray: this.fb.array([]) }); this.control = <FormArray>this.form.controls['emailsArray']; this.patch(); } private patch(): void { // iterate the object model and extra values, binding them to the controls this.emailsModel.emails.forEach((item) => { this.control.push(this.patchValues(item)); }) } 

This is what builds each input control (such as AbstracControl) with a validator:

  private patchValues(item): AbstractControl { return this.fb.group({ email: [item, Validators.compose([emailValidator])] }) } 

2 helper methods to check if the input has been affected, and if the validator raised an error (see the template to see how they are used), note that I am passing the array index value from *ngFor in the template)

  private hasError(i):boolean { // const control = <FormArray>this.form.controls['emailsArray']; return this.control.controls[i].get('email').hasError('invalidEmail'); } private isTouched(i):boolean { // const control = <FormArray>this.form.controls['emailsArray']; return this.control.controls[i].get('email').touched; } 

Here's the validator:

 export function emailValidator(control: FormControl): { [key: string]: any } { var emailRegexp = /[a-z0-9._%+-] +@ [a-z0-9.-]+\.[az]{2,3}$/; if (control.value && !emailRegexp.test(control.value)) { return { invalidEmail: true }; } } 

And the template:

 <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)" class="text-left"> <div formArrayName="emailsArray"> <div *ngFor="let child of form.controls.emailsArray.controls; let i=index"> <div class="form-group" formGroupName="{{i}}"> <input formControlName="email" class="form-control checking-field" placeholder="Email" type="text"> <span class="help-block" *ngIf="isTouched(i)"> <span class="text-danger" *ngIf="hasError(i)">Invalid email address </span> </span> </div> </div> </div> <pre>{{form.value | json }}</pre> <div class="form-group text-center"> <button class="btn btn-main btn-block" type="submit">INVITE</button> </div> </form> 

For what it's worth, I started with this terrible mess, but if you look at the code below, you can more easily understand the code above!

  public form: FormGroup; public email1: AbstractControl; public email2: AbstractControl; public email3: AbstractControl; public email4: AbstractControl; public email5: AbstractControl; constructor( fb: FormBuilder ) { this.form = fb.group({ 'email1': ['', Validators.compose([emailValidator])], 'email2': ['', Validators.compose([emailValidator])], 'email3': ['', Validators.compose([emailValidator])], 'email4': ['', Validators.compose([emailValidator])], 'email5': ['', Validators.compose([emailValidator])], }); this.email1 = this.form.controls['email1']; this.email2 = this.form.controls['email2']; this.email3 = this.form.controls['email3']; this.email4 = this.form.controls['email4']; this.email5 = this.form.controls['email5']; } 

and above 5 of these divs were used in the template - not very DRY!

 <div class="form-group"> <input [formControl]="email1" class="form-control checking-field" placeholder="Email" type="text"> <span class="help-block" *ngIf="form.get('email1').touched"> <span class="text-danger" *ngIf="form.get('email1').hasError('invalidEmail')">Invalid email address</span> </span> </div> 
+4
source

I think this is not possible with FormControlName .

You can use ngModel .. take a look at your modified plunker:

http://plnkr.co/edit/0DXSIUY22D6Qlvv0HF0D?p=preview

 @Component({ selector: 'my-app', template: ` <hr> <form [formGroup]="userForm" (ngSubmit)="submit(userForm.value)"> <input type="checkbox" formControlName="isRequired"> Required Field <div formGroupName="type"> <div formArrayName="options"> <div *ngFor="let option of userForm.controls.type.controls.options.controls; let i=index"> <label>{{ option.value.label }}</label><br /> <!-- change your textarea --> <textarea [name]="i" [(ngModel)]="option.value.value" [ngModelOptions]="{standalone: true}" ></textarea> </div> </div> </div> <button type="submit">Submit</button> </form> <br> <pre>{{userForm.value | json }}</pre> `, }) export class App { name:string; userForm: FormGroup; fields:any; ngOnInit() { this.fields = { isRequired: true, type: { options: [ { label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' } ] } }; this.userForm = this.fb.group({ isRequired: [this.fields.isRequired, Validators.required], //... here a lot of other controls type: this.fb.group({ // .. added map-function options: this.fb.array(this.fields.type.options.map(o => new FormControl(o))), }) }); } submit(value) { console.log(value); } constructor(private fb: FormBuilder) { } addNumber() { const control = <FormArray>this.userForm.controls['numbers']; control.push(new FormControl()) } } 
0
source

Source: https://habr.com/ru/post/1265809/


All Articles