-1

I am pretty new to angular and learning formArray. I have 2 simple lists of roles and rights. For this, I need to plot a checkbox matrix as follows. Also, at the bottom of every column, I need to show the total count of selected rights.

Sample output

following is the component.ts code that I have written to achieve the above logic.

 constructor(private fb: FormBuilder) {
  
  }

  roleRightsForm: FormGroup;
  
  roles = [
    { id: 1, name: 'Admin' },
    { id: 2, name: 'User' },
    // Add more roles here
  ];
  rights = [
    { id: 1, name: 'Create' },
    { id: 2, name: 'Read' },
    { id: 3, name: 'Update' },
    { id: 4, name: 'Delete' },
  ];

  initRoles(): void {

    this.roleRightsForm = this.fb.group({
      roles: this.fb.array([]),
    });
 
 
    const rolesFormArray = this.roleRightsForm.get('roles') as FormArray;
    this.roles.forEach(role => {
      const roleFormGroup = this.fb.group({
        role: [role],
        rights: this.fb.array([])
      });
        
      this.rights.forEach(right=>{
          const rightFormArray = roleFormGroup.get('rights') as FormArray;
          rightFormArray.push( new FormControl(false));
        })

      rolesFormArray.push(roleFormGroup);
      
    });
  }

  get  getRoles(){
    return (this.roleRightsForm.get('roles') as FormArray).controls;
  }

and following is the html code.

<form [formGroup]="roleRightsForm">
    <table>
      <thead>
        <tr>
          <th></th>
          <th *ngFor="let right of rights">{{ right.name }}</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let roleGroup of getRoles; let i = index" [formGroup]="roleGroup"  >
           
          <td>{{ roleGroup.value.role.name }}</td>

          <td *ngFor="let right of rights; let j = index"  >
            
            <input type="checkbox" [formControlName]="i + '-' + right.id">
          </td>
       
        </tr>
      </tbody>
    </table>
  </form>

But getting the following error.

main.ts:6 ERROR Error: Cannot find control with name: '0-1'
    at _throwError (forms.mjs:3150:11)
    at setUpControl (forms.mjs:2933:13)
    at FormGroupDirective.addControl (forms.mjs:4782:9)
    at FormControlName._setUpControl (forms.mjs:5370:43)
    at FormControlName.ngOnChanges (forms.mjs:5315:18)
    at FormControlName.rememberChangeHistoryAndInvokeOnChangesHook (core.mjs:2948:14)
    at callHookInternal (core.mjs:3940:14)
    at callHook (core.mjs:3971:9)
    at callHooks (core.mjs:3922:17)
    at executeInitAndCheckHooks (core.mjs:3872:9)

Need guidance on what I am doing wrong here. Also, to show the count under every column of selected rights, should I add a property "count" in "roleFormGroup" and on the checkbox change event, increase/decrease the count in the property? or is there a better way?

Sorry, if the questions are too basic. I am from a jQuery background and trying to wrap my head around Angulalr.

3
  • You have this line [formControlName]="i + '-' + right.id". You didn't declare [form array] Commented Aug 22, 2023 at 5:18
  • @Vega, can you please tell me what to add in [formArrayName]? Commented Aug 22, 2023 at 5:29
  • you need to declare the formControlName Commented Aug 22, 2023 at 5:34

1 Answer 1

0

a FormArray can be a FormArray of FormGroups, a FormArray of FormsControls or a FormArray of FormArray

Well you have a FormArray of FormGroups, one of the FormControls in the FormGroup is a FormArray of FormControls.

Always you have a FormArray you should use a "getter" of the formArray. When you have a formArray inside a FormArray you can not use a getter because you need an "index", so you need a function.

  get rolesFormArray() {
    return this.roleRightsForm.get('roles') as FormArray;
  }

  //this function get the formArrayRight at index
  getRightsFormArray(index: number) {
    return this.roleRightsForm.get('roles.'+index+'.rights') as FormArray
    //I use the "dot notation" with "get", but you can also use
    return this.rolesFormArray.at(i).get('rights') as FormArray
  }

Always you use a FormArray you should iterate over FormArray.controls (not over any variable or array)

<!--see the *ngIf, this avoid initials errors when roleRightsForm is undefined
-->
  <form *ngIf="roleRightsForm" [formGroup]="roleRightsForm">
    <!--see that you need say Angular you're using a formArray
        so give value to formArrayName-->
    <table formArrayName="roles">
      <thead>
        <tr>
          <th></th>
          <th *ngFor="let right of rights">{{ right }}</th>
        </tr>
      </thead>
      <tbody>
        <!--see that you iterate over formArray.controls
          and use formGroupName-->
        <tr
          *ngFor="let roleGroup of rolesFormArray.controls; let i = index"
          [formGroupName]="i"
        >
          <td>{{roles[i].name}}</td>
          <!--see that you need say Angular you're using a formArray
              in this case formArrayName is "rights"-->
          <ng-container formArrayName="rights">

            <!--here use the function that return the formArray
                as is a FormArray of FormControls, here not use 
                [formGroupName]-->
            <td
              *ngFor="let right of getRightsFormArray(i).controls; let j = index"
            >
              <!--you use [formControlName]="j"-->
              <input type="checkbox" [formControlName]="j" />
            </td>
          </ng-container>
        </tr>
      </tbody>
    </table>
  </form>

NOTE: if you see the value of the roleRightsForm.value.roles you see that have a value "role" that it's an object, but have no input asociated

a stackblitz

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.