Search code examples
angularangular-reactive-formsngfor

Angular Form Array access static data


I am using Reactive Forms to create a page for managing roles and permissions in my application. There are multiple tables of checkboxes that turn on or off a permission on a role, and I am trying to dynamically create these elements using *ngFor. The problem is, my form element only has a boolean value (on or off) and there is corresponding metadata that is needed (display name, list of claims, etc.) that I need to retrieve to display the element properly. My problem is, as I am looping through the form control collection, I need to match it up to the view model (essentially need to get a variable set in my html template for both the model and the form control.

Here is an example of what the page looks like: enter image description here

here is my form builder:

this.settingsForm = this.fb.group({
  roleName: this.exampleRole.name,
  dashboard: '',
  settingsPermissions: this.fb.array(this.settingsPermissions.map(perm => this.fb.group({
    name: perm.title, 
    permission: perm.val(this.exampleRole.permissions),
  }))),
  customerProfilePermissions: this.fb.group({
    //accessProfiles: {value: true, description:"Some test description", displayName: "Access Customer Profiles" },
    accessProfiles: true,
    accessPII: false,
    editProfiles: false,
    linkProfiles: false,

my example definition for the settings permission object:

export class PermissionConfiguration {
    title!: string;
    permissionClaims: string[] = [];
    description: string;
}

and an example of what my template looks like:

<div *ngIf="settingsForm.get('customerProfilePermissions.accessProfiles').value">
   <div class="row">
       <div class="col-3 col-xl-2">Access To PII </div>
            <div class="col-3 col-xl-2"><k-checkbox formControlName="accessPII"></k-checkbox></div>
            <div class="col-3 col-xl-2">Ability to view customer personally identifiable information.</div>
        </div>
        <div class="row">
            <div class="col-3 col-xl-2">Edit Profiles </div>
            <div class="col-3 col-xl-2"><k-checkbox formControlName="editProfiles"></k-checkbox></div>
            <div class="col-3 col-xl-2">Ability to edit certain information on the customer profile tab. Editable fields are defined on a per-client basis during implementation.</div>
        </div>
        <div class="row">
            <div class="col-3 col-xl-2">Link Related Profiles </div>
            <div class="col-3 col-xl-2"><k-checkbox formControlName="linkProfiles"></k-checkbox></div>
            <div class="col-3 col-xl-2">Ability to add/remove related profiles in the top right of each customer profile.</div>
        </div>

what I need is to be able to get the PermissionConfiguration object from the component along with the form control. If I can do that, then I can replace the name and description fields and make the form dynamic.

In the settings Permissions section (different model object) I did the following and it works but is highly inefficient:

<tr formArrayName="settingsPermissions"
                    *ngFor="let info of settingsForm.get('settingsPermissions')['controls']; let i = index;">
   <ng-container [formGroupName]="i">
        <td>{{ getPermModel(info.value.name).title }}</td>
        <td><k-checkmark-radio formControlName="permission" valueName="Hide" [groupName]="info.value.name" ></k-checkmark-radio></td>
        <td><k-checkmark-radio formControlName="permission" valueName="View" [groupName]="info.value.name"></k-checkmark-radio></td>
        <td><k-checkmark-radio formControlName="permission" valueName="Edit" [groupName]="info.value.name"></k-checkmark-radio></td>
        <td>{{ getPermModel(info.value.name).description}} </td>
    </ng-container>
</tr>

Update

GetPermModel Implementation:

getPermModel(name:string): SettingsPermission {
  let model = this.settingsPermissions.find(p => p.title == name);
  return model;
}

Solution

  • I can imaging two ways to improve it:

    1. Add a control with the required data to the group
    this.fb.array(this.settingsPermissions.map(perm => this.fb.group({
      name: perm.title, 
      permission: perm.val(this.exampleRole.permissions),
      permModel: this.fb.group({
         value: this.getPermModel(perm.title)),
         disabled: !this.isResetPassword
      }),
    }))),
    

    and use it in the HTML

    <tr formArrayName="settingsPermissions"
                        *ngFor="let info of settingsForm.get('settingsPermissions')['controls']; let i = index;">
       <ng-container [formGroupName]="i">
            <td>{{ info.value.permModel.title }}</td>
            <td><k-checkmark-radio formControlName="permission" valueName="Hide" [groupName]="info.value.name" ></k-checkmark-radio></td>
            <td><k-checkmark-radio formControlName="permission" valueName="View" [groupName]="info.value.name"></k-checkmark-radio></td>
            <td><k-checkmark-radio formControlName="permission" valueName="Edit" [groupName]="info.value.name"></k-checkmark-radio></td>
            <td>{{ info.value.permModel.description}} </td>
        </ng-container>
    </tr>
    

    The form value would contain the permModel field, which might be not desirable.

    1. create a pure pipe instead of the method getPermModel
    @Pipe({
      name: 'myCustomPipe', 
      pure: true        <----- here (default is `true`)
    })
    export class MyCustomPipe {
       constructor(private settingsPermissions) { }
    
       transform(name:string): SettingsPermission {
          let model = this.settingsPermissions.find(p => p.title == name);
          return model;
       }
    }
    

    settingsPermissions might be also a pipe param if it's not a good candidate to be injectable.