2

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;
}
4
  • Are you asking how to make accessing that object in the template more efficient? Commented Jul 20, 2021 at 15:49
  • I am asking how to get the model object for the form control inside the ngFor loop. the way I did it, with the function, runs every time change detection occurs and is only suitable for the very small number of items that I have in there right now Commented Jul 20, 2021 at 15:51
  • Ah I see, so this: <td>{{ getPermModel(info.value.name).description}} </td> is the line you are trying to get rid of? Can you post the implementation of getPermModel(...)? Commented Jul 20, 2021 at 15:57
  • No problem, just added it Commented Jul 20, 2021 at 16:04

2 Answers 2

1

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.

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

Comments

1

If I am understanding the issue correctly you are trying to eliminate this line because you correctly identified that Angular's change detection is going to make the function call expensive,

<td>{{ getPermModel(info.value.name).description}} </td>

I recommend writing a custom angular pipe to do that processing for you. You can pass in your settings configuration and your form value, and do all of the processing that getPermModel() function does with the benefit of the pipe only running when the input changes, rather than running on every change detection cycle like it will with the function in the template.

Here are the basics of implementing a pipe, https://angular.io/guide/pipes

It will look something like this,

@Pipe({name: 'getPermModel'})
export class GetPermModelPipe implements PipeTransform {
  transform(name: string, settings: PermissionsConfiguration): string {
    return this.settings.find(p => p.title == name);
  }
}

Then in your template,

<td>{{ (info.value.name | getPermModel : settingsConfig ).description}} </td>

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.