0

I am trying to populate a select form field from API call. I get the data but cannot seem to set the value correctly utilizing a FormArray.

So, I have 3 levels nested FormArray in angular which I took reference from here - https://stackblitz.com/edit/nested-formarrays-3-levels? This form is divided beautifully into X, Y, Z level.

Now, the complications started when I added W at X level in which I added dependent select form fields of state and city. And then I added select form field at Y level and Z level which are also dependent. Here is my attempt - https://stackblitz.com/edit/hashtaagblog2-1-nhxmhz

My problems -

  • I am not able to populate the state and city data into select options from JSON data.
  • Y and Z level of select field form are dependent which is also not populating data from JSON data.

1 Answer 1

0

when we work with complex FormsArrays it's better

1.-To have functions "get" that return the formArrays, see that the "XsArray" can be a getter, the others need be a normal function because need an index. So

  get XsArray() {
    return this.form.get("Xs") as FormArray;
  }
  getYsArray(index) {
    return this.XsArray.at(index).get("Ys") as FormArray;
  }
  getWsArray(index) {
    return this.XsArray.at(index).get("Ws") as FormArray;
  }
  getZsArray(index, indexz) {
    return this.getYsArray(index).at(indexz).get("Zs") as FormArray;
  }

See that you can use the function XsArray in the function getWsArray, e.g.

2.-The other thing is to have functions that return formGroup. This functions are feed with an object or with null. If is feed with null we add an object by default. Here are:

setXs(el:any=null)
 {
   el=el||{X:null}
   return this.fb.group({
     X:[el.X,Validators.required],
     Ys:el.Ys?this.fb.array(el.Ys.map(x=>this.setYs(x))):this.fb.array([]),
     Ws:el.Ws?this.fb.array(el.Ws.map(x=>this.setWs(x))):this.fb.array([])
   })
 }
 setYs(el:any=null)
 {
  el=el || {product:null}
  return this.fb.group({
    product:[el.product, [Validators.required]],
    Zs:el.Zs?this.fb.array(el.Zs.map(x=>this.setZs(x))):this.fb.array([])
  })
 }
 setWs(el:any=null){
   el=el || {state:null,city:null}
   return this.fb.group({
      state: [el.state, [Validators.required]],
      city: [el.city, [Validators.required]]
   })
 }
 setZs(el:any=null)
 {
   el=el || {Z:null}
   return this.fb.group({
     Z:[el.Z, [Validators.required, Validators.pattern("[0-9]{3}")]]
   })
 }

Hey! wait a moment what on earth mean this strange: this.fb.array(el.Zs.map(x=>this.setZs(x))). Well, el.Zs is an array, we transform each element of the array in a FormGroup (remember that this.setZs(x) return a formGroup) and with the array of formGroup we create the formArray. I know that sound complex we coud make in severals steps

//this.fb.array(el.Zs.map(x=>this.setZs(x)))
//is the same that
  let arr=this.fb.array([])
  el.Zs.forEach(x=>{
     arr.push(this.setZs(x)
  })

Well, now it's all more comfortable. See how the functions to add new elements becomes so easy like

  addX() {
    this.XsArray.push(this.setXs());
  }

  addY(ix) {
    this.getYsArray(ix).push(this.setYs());
  }

  addZ(ix, iy) {
    this.getZsArray(ix,iy).push(this.setZs());
  }

  addW(ix) {
    this.getYsArray(ix).push(this.setWs())
  }

An to remove so simple as, e.g.

  removeW(ix, iw) {
    this.getWsArray(ix).removeAt(iw)
  }

Well, At least, in ngOnInit we use the seedData like

this.form = this.fb.group({
  Xs: this.fb.array(this.seedData.Xs.map(x=>this.setXs(x)))
});

When we show in .html we use also the functions before explained iterating over the formArray.controls

<form [formGroup]="form">
  <div formArrayName="Xs">
    <div *ngFor="let X of XsArray.controls; let ix=index">
      <div [formGroupName]="ix" class="Xs">
        <input type="text" formControlName="X">
        ...
        <div formArrayName="Ys">
          <div *ngFor="let Y of getYsArray(ix).controls; let iy=index">
            <div [formGroupName]="iy" class="Ys">
              ...
              <div formArrayName="Zs">
                <div *ngFor="let Z of getZsArray(ix,iy).controls; let iz=index">
                  <div [formGroupName]="iz" class="Zs">
                   ....
                  </div>
                </div>
                ...
              </div>
            </div>
          </div>
          ...
        </div>
        <div formArrayName="Ws">
          <div *ngFor="let W of getWsArray(ix).controls; let iw=index">
            <div [formGroupName]="iw" class="Ws">
                ....
            </div>
          </div>
          <input type="button" (click)="addW(ix)" value="Add W">
        </div>
      </div>
    </div>
    ...
  </div>
  <form>

Well, the stackblitz is here

NOTE: A FormArray can be a FormArray of FormGroup or a FormArray of FormControls, if only has a unique property (e.g. the FormArray "Zs"), generally you should use a FormArray of FormControls

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.