Home > other >  Value appears in all dynamic input fields
Value appears in all dynamic input fields

Time:06-16

I have a form that contains dynamic input fields. So there is a button and when I press that button, a new input field will be created.

<form [formGroup]="userProfile" >
    <div *ngFor="let field of fields">
        <input id="username" formControlName="username" type="text" placeholder="Type something here...">

    </div>
    <button (click)="fields.push(fields.length)">Add Field</button>
</form>
this.userProfile =  this.fb.group({
  username: ['']
});

As you see, username has no default value but my problem is: When I enter a text in this input field and then press the button to create a new input field, the previously entered value also appears for the newly created input field.

I know that I can write a function that removes the value for the newly created field but this is more like a hack to me. So I am looking for a cleaner way.

Test it on StackBlitz.

CodePudding user response:

I would recommend creating a child component that will house each form and keeping an array control in the parent component. Basic example below, and you'll need to handle adding any pre-existing data. May not be the best approach, but it's worked well for me.

export class ParentComponent implements OnInit {
  public formGroup: FormGroup;
  constructor(private formBuilder: FormBuilder) {
    this.formGroup = this.formBuilder.group({
      myChildData: this.formBuilder.array([])
    })
 
  }

  public get getFormArray() {
    return this.formGroup.get('myChildData') as FormArray;
  }

  public addChildForm() {
    this.getFormArray.push(
      this.formBuilder.group({
        // build your child form here
      })
    )
  }
}
export class ChildComponent implement OnInit {
  @Input('form')
  public abstractControl!: AbstractControl;

  @Input('formIndex')
  public index!: number;

  @Output('onRemoveForm')
  public eventEmitter: EventEmitter<number> = new EventEmitter<number>();

  public formGroup!: FormGroup;

  ngOnInit() {
    this.formGroup = this.abstractControl as FormGroup;
  }

  public removeChildForm() {
    this.eventEmitter.emit(this.index);
  }
}
<!-- Parent HTML -->
<my-child-component 
  *ngFor="let control of getFormArray.controls; let i = index"
  [formIndex]="i"
  [form]="control">
</my-child-component>

CodePudding user response:

The values will be synchronized because all of your inputs have the same form control name. They also all have the same id which is not good.

I think you are looking for a solution like this:

  counter = 1;
  fields = [];
  userProfile = new FormGroup({
    username: new FormControl(''),
    address: new FormControl(''),
  });

  ngOnInit() {
    this.refreshFields();
  }

  refreshFields() {
    this.fields = Object.getOwnPropertyNames(this.userProfile.controls);
  }

  addField() {
    const name = `extra-field-${this.counter  }`;
    this.userProfile.addControl(name, new FormControl(''));
    this.refreshFields();
  }
<form [formGroup]="userProfile">
  <div *ngFor="let field of fields">
    <input
      id="{{ field }}"
      formControlName="{{ field }}"
      type="text"
      placeholder="Type something here..."
    />
  </div>
  <button (click)="addField()">Add Field</button>
</form>

I use a counter to generate a unique name for each new field, but you can do it however you want. Just need to make sure you don't get any naming collisions.

This also allows deleting fields like so:

  deleteField(name: string) {
    this.userProfile.removeControl(name);
    this.refreshFields();
  }

Stackblitz: https://stackblitz.com/edit/angular-formbuilder-z3w5a6?file=src/app/app.component.ts

  • Related