简体   繁体   中英

Angular Reactive Form with dynamic fields from array

I am using Angular 9, and am trying to implement a component that uses reactive forms .

I have the following:

approval-edit.component.ts

  public nominationAllOf: NominationAllOf[];
  public approvalEditForm: FormGroup;

  ngOnInit(): void {
    this.approvalEditForm = new FormGroup({
      userName00: new FormControl(),
      userName01: new FormControl(),
      userName02: new FormControl(),
      userName10: new FormControl(),
      userName11: new FormControl(),
      userName12: new FormControl()
   })
  }

This works, however, you can see that the form group values need to be dynamic according to the values in an array ( nominationAllOf ).

nominationAllOf is a 2 dimensional array.

ie

export interface NominationAllOf {
    nominations: NominationAnyOf[];
}

and

export interface NominationAnyOf {
    name: string;
}

Question

Is it possible to populate the FormGroup dynamically?

eg (this does not work)

    this.approvalEditForm = new FormGroup({
      for (i = 0; i < nominationAllOf.length; i++) {
           for (j = 0; j < nominationAllOf[i].length; j++) {
               'userName'+i+''+j: new FormControl(nominationAllOf[i].nominationAnyOf[j].name)
           }
      }
      
   })
  }

The following works (ie addControl ):

  private loadForm(): void {
    for (let i = 0; i < this.nominationAllOf.length; i++) {
      let nominationAnyOf: NominationAnyOf[] = this.nominationAllOf[i].nominations;
      for (let j = 0; j < nominationAnyOf.length; j++) {
          let name = nominationAnyOf[j].evaluatorInfo.personalInfo.name.firstName;
          this.approvalEditForm.addControl('userName'+i+''+j, new FormControl(name));
        }
      }
  }

There is a way to make this work with FormArray. You have it documented here: https://netbasal.com/angular-reactive-forms-the-ultimate-guide-to-formarray-3adbe6b0b61a

For your example it would work like this:

this.approvalEditForm = new FormGroup({
  userNames: new FormArray([
    new FormControl(),
    new FormControl(),
    new FormControl(),
    new FormControl(),
  ]),
});

Since you have array of elements just populate them with map() method initialy, and when you are rendering them you will use ngFor and you will display them in that order. If you want to add dynamically just push one of them from that FormArray like you would with regular array, or if you want to remove one you can use removeAt(indexNumber) method from FromArray. You also have insert and clear methods for adding or clearing all elements from FromArray

Use Bracket notation to create dynamic key.

  this.approvalEditForm = new FormGroup({
      for (i = 0; i < nominationAllOf.length; i++) {
           for (j = 0; j < nominationAllOf[i].length; j++) {
               ['userName'+i+''+j]: new FormControl(nominationAllOf[i].nominationAnyOf[j].name)
           }
      }
      
   })
  }

How about the following:

constructor(private fb: FormBuilder) { }

ngOnInit(): void {
  this.approvalEditForm = this.fb.group(
    this.nominationAllOf
      .map((x, xi) => x.nominations.map(({ name }, yi) => ({ [`userName${xi}${yi}`]: [name] })))
      .reduce((x, y) => ({ ...x, ...y.reduce((a, b) => ({ ...a, ...b }), {}) }), {}) // double flattening 
  )
}

Alternatively using Flat

this.approvalEditForm = new FormGroup(
  this.nominationAllOf
    .map((x, xi) => x.nominations.map(({ name }, yi) => ({ [`userName${xi}${yi}`]: new FormControl(name) })))
    .flat()
    .reduce((x, y) => ({ ...x, ...y }), {})
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM