Search code examples
arraysangularangular-forms

How to reassign the nested array keys based on Angular Reactive Forms?


I am working to recreate the nested array for Angular Reactive Forms.

Existing Nested Array.

nestedArray = [{
    id:'abc',
    required:true,
    children:[{
        id:"bcd",
        parentId:"abc",
        required:true,
        children:[{
            id:"cde",
            parentId:"bcd",
            required:false,
            children:[{
                id:"def",
                parentId:"cde",
                required:true,
                children:[{
                    id:"efg",
                    parentId:"def",
                    required:false,                        
                }]
            },
            {
                id: "xyz",
                parentId: "cde",
                required: true,
            }]
        }]
    }]
}]

Recreate this array for Angular Reactive Forms

nestedArray= this.fb.group({
    abc: [''],
    bcd: this.fb.group({
      cde: [''],
      efg: [''],
    }),
  });

Above array is incorrect, looking for the right way to create the children in Angular Reactive Form.

Thanks


Solution

  • You need make a recursive function that return a FormControl or a FormGroup

      form = new FormArray(this.nestedArray.map(x=>{
        const group=new FormGroup({})
        group.addControl(x.id,this.getGroup(x))
        return group
        }));
    
      getGroup(data: any) {
        if (!data.children)
          return new FormControl()
        const group = new FormGroup({});
        if (data.children) {
          
          data.children.forEach(x=>{
            group.addControl(x.id,this.getGroup(x))
          })
        }
        return group;
      }
    

    See stackblitz

    Update really the answer is wrong, becaouse we has no form control to the "parents". the function must be like

      form = new FormArray(this.nestedArray.map(x=>this.getGroup(x)))
      getGroup(data: any) {
        if (!data.children)
        { 
          return new FormControl)
        }
        const group = new FormGroup({});
        group.addControl(data.id,new FormControl())
        if (data.children) {
          
          data.children.forEach(x=>{
            group.addControl(x.id,this.getGroup(x))
          })
        }
        return group;
      }
    

    Well, the question is how conrol this crazy form. We can try create a "recursive template", but I think it's better another aproach. Imagine you has an array of elements, each one with three porperties: "index","label" and "control". And control is a reference to the FormControl of our form. Then we can construct an .html like

    <div *ngFor="let item of controls">
        <span [style.margin-left]="item.index*10+'px'">{{item.label}}</span>
        <input [formControl]="item.control">
        <span *ngIf="item.control.errors?.required">*</span>
    </div>
    

    easy no? just use directly [formControl] and item.control.errors or item.control.touched

    Well, for this first declare an empty array

      controls:any[]=[];
    

    And change the fuction so we add the group in two steps: 1.-create the formControl, 2.-Add to the group, the third step is add to our array an object with {label,control and "index"}. Well it's typical when we has a recursive function to want know the "index" of the recursion.

      //we pass as index 0 at first
      form = new FormArray(this.nestedArray.map(x=>this.getGroup(x,0)))
      getGroup(data: any,index:number) {
        if (!data.children)
        { 
          //create the control
          const control=new FormControl('',data.required?Validators.required:null)
    
          //add to the array this.controls
          this.controls.push({control:control,label:data.id,index:index})
    
          //return control
          return control
        }
        const group = new FormGroup({});
        index++
        //create the control
        const control=new FormControl('',data.required?Validators.required:null)
    
        //add to the array this.controls
        this.controls.push({control:control,label:data.id,index:index})
    
        //add the control to the group
        group.addControl(data.id,control)
    
        if (data.children) {
          data.children.forEach(x=>{
            group.addControl(x.id,this.getGroup(x,index++))
          })
        }
        return group;
      }
    

    Well, In the stackblitz add the property "value" to our complex object, to show how can do it, and we use as "label" the "id" -perhafs our complex object need a new property label-