How to Replace PermissionManagementComponent
Run the following command in angular
folder to create a new component called PermissionManagementComponent
.
yarn ng generate component permission-management --entryComponent --inlineStyle
# You don't need the --entryComponent option in Angular 9
Open the generated permission-management.component.ts
in src/app/permission-management
folder and replace the content with the following:
import {
Component,
EventEmitter,
Input,
Output,
Renderer2,
TrackByFunction,
Inject,
Optional,
} from '@angular/core';
import { ReplaceableComponents } from '@abp/ng.core';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { finalize, map, pluck, take, tap } from 'rxjs/operators';
import {
GetPermissions,
UpdatePermissions,
PermissionManagement,
PermissionManagementState,
} from '@abp/ng.permission-management';
type PermissionWithMargin = PermissionManagement.Permission & {
margin: number;
};
@Component({
selector: 'app-permission-management',
templateUrl: './permission-management.component.html',
styles: [
`
.overflow-scroll {
max-height: 70vh;
overflow-y: scroll;
}
`,
],
})
export class PermissionManagementComponent
implements
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs {
protected _providerName: string;
@Input()
get providerName(): string {
if (this.replaceableData) return this.replaceableData.inputs.providerName;
return this._providerName;
}
set providerName(value: string) {
this._providerName = value;
}
protected _providerKey: string;
@Input()
get providerKey(): string {
if (this.replaceableData) return this.replaceableData.inputs.providerKey;
return this._providerKey;
}
set providerKey(value: string) {
this._providerKey = value;
}
protected _hideBadges = false;
@Input()
get hideBadges(): boolean {
if (this.replaceableData) return this.replaceableData.inputs.hideBadges;
return this._hideBadges;
}
set hideBadges(value: boolean) {
this._hideBadges = value;
}
protected _visible = false;
@Input()
get visible(): boolean {
return this._visible;
}
set visible(value: boolean) {
if (value === this._visible) return;
if (value) {
this.openModal().subscribe(() => {
this._visible = true;
this.visibleChange.emit(true);
if (this.replaceableData) this.replaceableData.outputs.visibleChange(true);
});
} else {
this.selectedGroup = null;
this._visible = false;
this.visibleChange.emit(false);
if (this.replaceableData) this.replaceableData.outputs.visibleChange(false);
}
}
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Select(PermissionManagementState.getPermissionGroups)
groups$: Observable<PermissionManagement.Group[]>;
@Select(PermissionManagementState.getEntityDisplayName)
entityName$: Observable<string>;
selectedGroup: PermissionManagement.Group;
permissions: PermissionManagement.Permission[] = [];
selectThisTab = false;
selectAllTab = false;
modalBusy = false;
trackByFn: TrackByFunction<PermissionManagement.Group> = (_, item) => item.name;
get selectedGroupPermissions$(): Observable<PermissionWithMargin[]> {
return this.groups$.pipe(
map((groups) =>
this.selectedGroup
? groups.find((group) => group.name === this.selectedGroup.name).permissions
: []
),
map<PermissionManagement.Permission[], PermissionWithMargin[]>((permissions) =>
permissions.map(
(permission) =>
(({
...permission,
margin: findMargin(permissions, permission),
isGranted: this.permissions.find((per) => per.name === permission.name).isGranted,
} as any) as PermissionWithMargin)
)
)
);
}
get isVisible(): boolean {
if (!this.replaceableData) return this.visible;
return this.replaceableData.inputs.visible;
}
constructor(
@Optional()
@Inject('REPLACEABLE_DATA')
public replaceableData: ReplaceableComponents.ReplaceableTemplateData<
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs
>,
private store: Store
) {}
getChecked(name: string) {
return (this.permissions.find((per) => per.name === name) || { isGranted: false }).isGranted;
}
isGrantedByOtherProviderName(grantedProviders: PermissionManagement.GrantedProvider[]): boolean {
if (grantedProviders.length) {
return grantedProviders.findIndex((p) => p.providerName !== this.providerName) > -1;
}
return false;
}
onClickCheckbox(clickedPermission: PermissionManagement.Permission, value) {
if (
clickedPermission.isGranted &&
this.isGrantedByOtherProviderName(clickedPermission.grantedProviders)
)
return;
setTimeout(() => {
this.permissions = this.permissions.map((per) => {
if (clickedPermission.name === per.name) {
return { ...per, isGranted: !per.isGranted };
} else if (clickedPermission.name === per.parentName && clickedPermission.isGranted) {
return { ...per, isGranted: false };
} else if (clickedPermission.parentName === per.name && !clickedPermission.isGranted) {
return { ...per, isGranted: true };
}
return per;
});
this.setTabCheckboxState();
this.setGrantCheckboxState();
}, 0);
}
setTabCheckboxState() {
this.selectedGroupPermissions$.pipe(take(1)).subscribe((permissions) => {
const selectedPermissions = permissions.filter((per) => per.isGranted);
const element = document.querySelector('#select-all-in-this-tabs') as any;
if (selectedPermissions.length === permissions.length) {
element.indeterminate = false;
this.selectThisTab = true;
} else if (selectedPermissions.length === 0) {
element.indeterminate = false;
this.selectThisTab = false;
} else {
element.indeterminate = true;
}
});
}
setGrantCheckboxState() {
const selectedAllPermissions = this.permissions.filter((per) => per.isGranted);
const checkboxElement = document.querySelector('#select-all-in-all-tabs') as any;
if (selectedAllPermissions.length === this.permissions.length) {
checkboxElement.indeterminate = false;
this.selectAllTab = true;
} else if (selectedAllPermissions.length === 0) {
checkboxElement.indeterminate = false;
this.selectAllTab = false;
} else {
checkboxElement.indeterminate = true;
}
}
onClickSelectThisTab() {
this.selectedGroupPermissions$.pipe(take(1)).subscribe((permissions) => {
permissions.forEach((permission) => {
if (permission.isGranted && this.isGrantedByOtherProviderName(permission.grantedProviders))
return;
const index = this.permissions.findIndex((per) => per.name === permission.name);
this.permissions = [
...this.permissions.slice(0, index),
{ ...this.permissions[index], isGranted: !this.selectThisTab },
...this.permissions.slice(index + 1),
];
});
});
this.setGrantCheckboxState();
}
onClickSelectAll() {
this.permissions = this.permissions.map((permission) => ({
...permission,
isGranted:
this.isGrantedByOtherProviderName(permission.grantedProviders) || !this.selectAllTab,
}));
this.selectThisTab = !this.selectAllTab;
}
onChangeGroup(group: PermissionManagement.Group) {
this.selectedGroup = group;
this.setTabCheckboxState();
}
submit() {
this.modalBusy = true;
const unchangedPermissions = getPermissions(
this.store.selectSnapshot(PermissionManagementState.getPermissionGroups)
);
const changedPermissions: PermissionManagement.MinimumPermission[] = this.permissions
.filter((per) =>
unchangedPermissions.find((unchanged) => unchanged.name === per.name).isGranted ===
per.isGranted
? false
: true
)
.map(({ name, isGranted }) => ({ name, isGranted }));
if (changedPermissions.length) {
this.store
.dispatch(
new UpdatePermissions({
providerKey: this.providerKey,
providerName: this.providerName,
permissions: changedPermissions,
})
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.visible = false;
});
} else {
this.modalBusy = false;
this.visible = false;
}
}
openModal() {
if (!this.providerKey || !this.providerName) {
throw new Error('Provider Key and Provider Name are required.');
}
return this.store
.dispatch(
new GetPermissions({
providerKey: this.providerKey,
providerName: this.providerName,
})
)
.pipe(
pluck('PermissionManagementState', 'permissionRes'),
tap((permissionRes: PermissionManagement.Response) => {
this.selectedGroup = permissionRes.groups[0];
this.permissions = getPermissions(permissionRes.groups);
})
);
}
initModal() {
this.setTabCheckboxState();
this.setGrantCheckboxState();
}
onVisibleChange(visible: boolean) {
this.visible = visible;
if (this.replaceableData) {
this.replaceableData.inputs.visible = visible;
this.replaceableData.outputs.visibleChange(visible);
}
}
}
function findMargin(
permissions: PermissionManagement.Permission[],
permission: PermissionManagement.Permission
) {
const parentPermission = permissions.find((per) => per.name === permission.parentName);
if (parentPermission && parentPermission.parentName) {
let margin = 20;
return (margin += findMargin(permissions, parentPermission));
}
return parentPermission ? 20 : 0;
}
function getPermissions(groups: PermissionManagement.Group[]): PermissionManagement.Permission[] {
return groups.reduce((acc, val) => [...acc, ...val.permissions], []);
}
Open the generated permission-management.component.html
in src/app/permission-management
folder and replace the content with the below:
<abp-modal
[visible]="isVisible"
(visibleChange)="onVisibleChange($event)"
(init)="initModal()"
[busy]="modalBusy"
>
<ng-container *ngIf="{ entityName: entityName$ | async } as data">
<ng-template #abpHeader>
<h4>
{{ 'AbpPermissionManagement::Permissions' | abpLocalization }} - {{ data.entityName }}
</h4>
</ng-template>
<ng-template #abpBody>
<div class="custom-checkbox custom-control mb-2">
<input
type="checkbox"
id="select-all-in-all-tabs"
name="select-all-in-all-tabs"
class="custom-control-input"
[(ngModel)]="selectAllTab"
(click)="onClickSelectAll()"
/>
<label class="custom-control-label" for="select-all-in-all-tabs">{{
'AbpPermissionManagement::SelectAllInAllTabs' | abpLocalization
}}</label>
</div>
<hr class="mt-2 mb-2" />
<div class="row">
<div class="overflow-scroll col-md-4">
<ul class="nav nav-pills flex-column">
<li *ngFor="let group of groups$ | async; trackBy: trackByFn" class="nav-item">
<a
class="nav-link pointer"
[class.active]="selectedGroup?.name === group?.name"
(click)="onChangeGroup(group)"
>{{ group?.displayName }}</a
>
</li>
</ul>
</div>
<div class="col-md-8 overflow-scroll">
<h4>{{ selectedGroup?.displayName }}</h4>
<hr class="mt-2 mb-3" />
<div class="pl-1 pt-1">
<div class="custom-checkbox custom-control mb-2">
<input
type="checkbox"
id="select-all-in-this-tabs"
name="select-all-in-this-tabs"
class="custom-control-input"
[(ngModel)]="selectThisTab"
(click)="onClickSelectThisTab()"
/>
<label class="custom-control-label" for="select-all-in-this-tabs">{{
'AbpPermissionManagement::SelectAllInThisTab' | abpLocalization
}}</label>
</div>
<hr class="mb-3" />
<div
*ngFor="
let permission of selectedGroupPermissions$ | async;
let i = index;
trackBy: trackByFn
"
[style.margin-left]="permission.margin + 'px'"
class="custom-checkbox custom-control mb-2"
>
<input
#permissionCheckbox
type="checkbox"
[checked]="getChecked(permission.name)"
[value]="getChecked(permission.name)"
[attr.id]="permission.name"
class="custom-control-input"
[disabled]="isGrantedByOtherProviderName(permission.grantedProviders)"
/>
<label
class="custom-control-label"
[attr.for]="permission.name"
(click)="onClickCheckbox(permission, permissionCheckbox.value)"
>{{ permission.displayName }}
<ng-container *ngIf="!hideBadges">
<span
*ngFor="let provider of permission.grantedProviders"
class="badge badge-light"
>{{ provider.providerName }}: {{ provider.providerKey }}</span
>
</ng-container>
</label>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<abp-button iconClass="fa fa-check" (click)="submit()">{{
'AbpIdentity::Save' | abpLocalization
}}</abp-button>
</ng-template>
</ng-container>
</abp-modal>
Open app.component.ts
in src/app
folder and modify it as shown below:
import { AddReplaceableComponent } from '@abp/ng.core';
import { ePermissionManagementComponents } from '@abp/ng.permission-management';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngxs/store';
import { PermissionManagementComponent } from './permission-management/permission-management.component';
//...
export class AppComponent implements OnInit {
constructor(private store: Store) {} // injected store
ngOnInit() {
// added dispatching the AddReplaceableComponent action
this.store.dispatch(
new AddReplaceableComponent({
component: PermissionManagementComponent,
key: ePermissionManagementComponents.PermissionManagement,
})
);
}
}