Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to typing more than 1 character in input for FormArray within a FormArray for Reactive Forms

I am thinking this could be a bug for Reactive Form. I would appreciate any help from more experienced Angular Experts.

Symptom: Unable to key in more than 1 character in input at given time.

Occurrence: When input is a FormArray within a FormArray

I have included a Plunker link as below: https://embed.plnkr.co/zl2BQe/.

//app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
 })
export class AppModule { }

//app.component.ts
import { Component, OnInit} from '@angular/core';
import { FormArray, FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  testForm: FormGroup; 

  ngOnInit(){
   this.testForm = new FormGroup({
   'lines': new FormArray([
    new FormGroup({
      'fields': new FormArray([
        new FormControl('')
      ])
     })
    ])
   })
  }

onAddLine(){
if(this.testForm.value.lines.length<10){
  (<FormArray>this.testForm.get('lines')).push(
    new FormGroup({
      'fields': new FormArray([
        new FormControl()
      ])
     })
   )
 }
}

onAddField(){
  // Not developed       
 }
}

//app.component.html
<div class="panel panel-default">
  <div class="panel-heading">
    Angular Reactive FormArray Error 
    <!--<button class="btn btn-primary" (click)="onAddField()">Add 
    Field</button>-->
    <button class="btn btn-primary" (click)="onAddLine()">Add 
    Line</button>
  </div>
<div class="panel-body">
  <form [formGroup]="testForm">
    <div class="form-inline" *ngFor="let line of testForm.value.lines; let i = index" formArrayName="lines">
      <div class="form-group">
        <span [formGroupName]="i">
          <span *ngFor="let field of testForm.value.lines[0].fields; let j = index" formArrayName="fields">
              <input type="text" class="form-control" [formControlName]="j">
              Please Type on Input Field
          </span>
        </span>
      </div>
    </div>
  </form>
</div>

like image 473
edmondtm Avatar asked Oct 21 '25 15:10

edmondtm


1 Answers

Cause

You get this problem because you rely on formGroup.value which gets a new instance when any of the input value changes. Therefore your control tree will be recreated in DOM thus loosing the focus.

The steps which create this problem are:

  • you change the input which reflect the value directly to the FormControl value
  • the formGroup.value changes
  • *ngFor iterates over the new value and creates new elements removing the old ones
  • the new element will have the previous element value but not the focus

Why *ngFor creates new elements

NgFor creates new elements because of trackBy which is by default the enumerated value. If the value changes, the element from DOM, which is tracked by the older value, is considered to be obsolete, thus being removed, and a new element is created for the new value. Even if the value appears to be the same, it is not, is just another instance of FormControl, as you are actually enumerating over the FormControls.

In your case: *ngFor="let line of testForm.value.lines;... and *ngFor="let field of testForm.value.lines[0].fields;... are incorrect for this reason.

Solution

Here is an updated Plunker.

Therefore the template should look like:

  <div class="panel panel-default">
    <div class="panel-heading">
      Angular Reactive FormArray Error 
      <!--<button class="btn btn-primary" (click)="onAddField()">Add Field</button>-->
      <button class="btn btn-primary" (click)="onAddLine()">Add Line</button>
    </div>
    <div class="panel-body">
      <form [formGroup]="testForm">
        <div class="form-inline" *ngFor="let line of testForm.get('lines').controls; let i = index" formArrayName="lines">
          <div class="form-group">
            <span [formGroupName]="i">
              <span *ngFor="let field of line.get('fields').controls; let j = index" formArrayName="fields">
                  <input type="text" class="form-control" [formControlName]="j">
                  Please Type on Input Field
              </span>
            </span>
          </div>
        </div>
      </form>
    </div>
  </div>
like image 118
andreim Avatar answered Oct 23 '25 04:10

andreim



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!