- Create a new module called
IdentityExtendedModule
:
yarn ng generate module identity-extended
2.Run the following command to create a new component called UsersComponent
:
yarn ng generate component users --inlineStyle --module identity-extended
- Open the generated
identity-extended.module.ts
file insrc/app/identity-extended
and replace content with following:
import {NgModule} from '@angular/core';
import {IdentityModule} from '@volo/abp.ng.identity';
import {NgbNavModule, NgbTooltipModule} from '@ng-bootstrap/ng-bootstrap';
import {ThemeSharedModule} from '@abp/ng.theme.shared';
import {UiExtensionsModule} from '@abp/ng.theme.shared/extensions';
import {CoreModule} from '@abp/ng.core';
import {PermissionManagementModule} from '@abp/ng.permission-management';
import {NgxValidateCoreModule} from '@ngx-validate/core';
import {UsersComponent} from './users.component';
@NgModule({
declarations: [UsersComponent],
imports: [
IdentityModule.forChild(),
CoreModule,
ThemeSharedModule,
NgbNavModule,
PermissionManagementModule,
UiExtensionsModule,
NgbTooltipModule,
NgxValidateCoreModule
]
})
export class IdentityExtendedModule{}
- Open the generated
users.component.ts
file insrc/app/identity-extended
and replace content with following:
import {generatePassword, ListService, LocalizationService} from '@abp/ng.core';
import {Confirmation, ConfirmationService, getPasswordValidators, ToasterService} from '@abp/ng.theme.shared';
import {EXTENSIONS_IDENTIFIER, FormPropData, generateFormFromProps} from '@abp/ng.theme.shared/extensions';
import {Component, Injector, OnInit, TemplateRef, TrackByFunction, ViewChild} from '@angular/core';
import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
import {Select, Store} from '@ngxs/store';
import {Observable} from 'rxjs';
import {finalize, pluck, switchMap, take, tap} from 'rxjs/operators';
import snq from 'snq';
import {
CreateUser,
DeleteUser,
eIdentityComponents,
GetUserRoles,
GetUsers,
Identity,
IdentityService,
IdentityState,
OrganizationUnitWithDetailsDto,
UnlockUser,
UpdateUser,
UsersComponent as IdentityUsersComponent
} from '@volo/abp.ng.identity';
@Component({
selector: 'abp-users',
templateUrl: './users.component.html',
providers: [
ListService,
{
provide: EXTENSIONS_IDENTIFIER,
useValue: eIdentityComponents.Users,
},
{ provide: IdentityUsersComponent, useExisting: UsersComponent },
],
styles: [
`
.mh-35 {
max-height: 35px;
}
`,
],
})
export class UsersComponent implements OnInit {
@Select(IdentityState.getUsers)
data$: Observable<Identity.UserItem[]>;
@Select(IdentityState.getUsersTotalCount)
totalCount$: Observable<number>;
@ViewChild('modalContent')
modalContent: TemplateRef<any>;
form: FormGroup;
setPasswordForm = this.fb.group({
newPassword: ['', [Validators.required, ...getPasswordValidators(this.store)]],
});
selected: Identity.UserItem;
selectedUserRoles: Identity.RoleItem[];
roles: Identity.RoleItem[];
organizationUnits: OrganizationUnitWithDetailsDto[];
selectedOrganizationUnits: OrganizationUnitWithDetailsDto[];
visiblePermissions: boolean = false;
providerKey: string;
isModalVisible: boolean;
isSetPasswordModalVisible: boolean;
modalBusy: boolean = false;
visibleClaims: boolean = false;
claimSubject = {} as { id: string; type: 'roles' | 'users' };
trackByFn: TrackByFunction<AbstractControl> = (index, item) => Object.keys(item)[0] || index;
get roleGroups(): FormGroup[] {
return snq(() => (this.form.get('roleNames') as FormArray).controls as FormGroup[], []);
}
get organizationUnitGroups(): FormGroup[] {
return snq(
() => (this.form.get('organizationUnitIds') as FormArray).controls as FormGroup[],
[],
);
}
onVisiblePermissionChange = (value: boolean) => {
this.visiblePermissions = value;
};
constructor(
public readonly list: ListService,
private confirmationService: ConfirmationService,
private identityService: IdentityService,
private fb: FormBuilder,
private store: Store,
private toasterService: ToasterService,
private injector: Injector,
private localizationService: LocalizationService,
) {}
ngOnInit() {
this.hookToQuery();
}
private hookToQuery() {
this.list.hookToQuery(query => this.store.dispatch(new GetUsers(query))).pipe(tap(console.log)).subscribe();
}
buildForm() {
const data = new FormPropData(this.injector, this.selected);
this.form = generateFormFromProps(data);
this.identityService.getUserAssingableRoles().subscribe(({ items }) => {
this.roles = items;
this.form.addControl(
'roleNames',
this.fb.array(
this.roles.map(role =>
this.fb.group({
[role.name]: [
this.selected.id
? !!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))
: role.isDefault,
],
}),
),
),
);
});
this.identityService.getUserAvailableOrganizationUnits().subscribe(({ items }) => {
this.organizationUnits = items;
this.form.addControl(
'organizationUnitIds',
this.fb.array(
this.organizationUnits.map(unit =>
this.fb.group({
[unit.displayName]: [
this.selected.id
? !!snq(() => this.selectedOrganizationUnits.find(u => u.id === unit.id))
: false,
],
}),
),
),
);
});
}
openModal() {
this.buildForm();
this.isModalVisible = true;
}
onAdd() {
this.selected = {} as Identity.UserItem;
this.selectedUserRoles = [] as Identity.RoleItem[];
this.selectedOrganizationUnits = [] as OrganizationUnitWithDetailsDto[];
this.openModal();
}
onEdit(id: string) {
this.identityService
.getUserById(id)
.pipe(
tap(selectedUser => (this.selected = selectedUser)),
switchMap(() => this.store.dispatch(new GetUserRoles(id))),
pluck('IdentityState'),
tap(state => (this.selectedUserRoles = state.selectedUserRoles || [])),
switchMap(() => this.identityService.getUserOrganizationUnits(id)),
tap(res => (this.selectedOrganizationUnits = res)),
take(1),
)
.subscribe(() => this.openModal());
}
save() {
if (!this.form.valid) return;
this.modalBusy = true;
const { roleNames, organizationUnitIds } = this.form.value;
const mappedRoleNames = snq(
() =>
roleNames.filter(role => !!role[Object.keys(role)[0]]).map(role => Object.keys(role)[0]),
[],
);
const mappedOrganizationUnitIds = snq(
() =>
organizationUnitIds
.filter(unit => !!unit[Object.keys(unit)[0]])
.map(unit => this.organizationUnits.find(u => u.displayName === Object.keys(unit)[0]).id),
[],
);
this.store
.dispatch(
this.selected.id
? new UpdateUser({
...this.selected,
...this.form.value,
id: this.selected.id,
roleNames: mappedRoleNames,
organizationUnitIds: mappedOrganizationUnitIds,
})
: new CreateUser({
...this.form.value,
roleNames: mappedRoleNames,
organizationUnitIds: mappedOrganizationUnitIds,
}),
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.list.get();
this.isModalVisible = false;
});
}
delete(id: string, userName: string) {
this.confirmationService
.warn('AbpIdentity::UserDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', {
messageLocalizationParams: [userName],
})
.subscribe((status: Confirmation.Status) => {
if (status === Confirmation.Status.confirm) {
this.store.dispatch(new DeleteUser(id)).subscribe(() => this.list.get());
}
});
}
onManageClaims(id: string) {
this.claimSubject = {
id,
type: 'users',
};
this.visibleClaims = true;
}
unlock(id: string) {
this.store.dispatch(new UnlockUser(id)).subscribe(() => {
this.toasterService.success('AbpIdentity::UserUnlocked');
this.list.get();
});
}
openPermissionsModal(providerKey: string) {
this.providerKey = providerKey;
setTimeout(() => {
this.visiblePermissions = true;
}, 0);
}
setPassword() {
if (this.setPasswordForm.invalid) return;
this.modalBusy = true;
this.identityService
.changePassword(this.selected.id, this.setPasswordForm.value)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.isSetPasswordModalVisible = false;
this.selected = {} as Identity.UserItem;
this.setPasswordForm.reset();
});
}
generatePassword() {
this.setPasswordForm.get('newPassword').setValue(generatePassword());
}
getParentName(parentId: string) {
if (!parentId) return undefined;
const { displayName } = this.organizationUnits.find(unit => unit.id === parentId);
return this.localizationService.instant('AbpIdentity::OrganizationUnit:Parent{0}', displayName);
}
}
- Open the generated
users.component.html
file insrc/app/identity-extended
and replace content with following:
<ng-container *ngIf="data$ | async as data">
<div class="row entry-row">
<div class="col-auto">
<h1 class="content-header-title">{{ 'AbpIdentity::Users' | abpLocalization }}</h1>
</div>
<div class="col-lg-auto pl-lg-0">
<abp-breadcrumb></abp-breadcrumb>
</div>
<div class="col">
<abp-page-toolbar [record]="data"></abp-page-toolbar>
</div>
</div>
<div id="identity-users-wrapper">
<div class="card">
<div class="card-body">
<div id="data-tables-table-filter" class="data-tables-filter">
<div class="input-group">
<input
type="search"
class="form-control"
[placeholder]="'AbpUi::PagerSearch' | abpLocalization"
[(ngModel)]="list.filter"
/>
<div class="input-group-append">
<button class="btn btn-sm btn-primary" (click)="list.get()">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</div>
<abp-extensible-table
actionsText="AbpIdentity::Actions"
[data]="data"
[recordsTotal]="totalCount$ | async"
[list]="list"
></abp-extensible-table>
</div>
</div>
</div>
</ng-container>
<abp-modal [(visible)]="isModalVisible" [busy]="modalBusy" (disappear)="form = null">
<ng-template #abpHeader>
<h3>{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewUser') | abpLocalization }}</h3>
</ng-template>
<ng-template #abpBody>
<form *ngIf="form" [formGroup]="form" (ngSubmit)="save()" validateOnSubmit>
<ul id="user-nav-tabs" ngbNav #nav="ngbNav" class="nav-tabs">
<li id="user-informations" ngbNavItem>
<a ngbNavLink>{{ 'AbpIdentity::UserInformations' | abpLocalization }}</a>
<ng-template ngbNavContent
><abp-extensible-form [selectedRecord]="selected"></abp-extensible-form
></ng-template>
</li>
<li id="user-roles" ngbNavItem>
<a ngbNavLink>{{ 'AbpIdentity::Roles' | abpLocalization }}</a>
<ng-template ngbNavContent>
<div
*ngFor="let roleGroup of roleGroups; let i = index; trackBy: trackByFn"
class="custom-checkbox custom-control mb-2"
>
<input
type="checkbox"
class="custom-control-input"
[attr.id]="'roles-' + i"
[formControl]="roleGroup.controls[roles[i].name]"
/>
<label class="custom-control-label" [attr.for]="'roles-' + i">{{
roles[i].name
}}</label>
</div></ng-template
>
</li>
<li id="user-organization-units" ngbNavItem>
<a ngbNavLink>{{ 'AbpIdentity::OrganizationUnits' | abpLocalization }}</a>
<ng-template ngbNavContent>
<div
*ngFor="let unitGroup of organizationUnitGroups; let i = index; trackBy: trackByFn"
class="custom-checkbox custom-control mb-2"
>
<input
type="checkbox"
class="custom-control-input"
[attr.id]="'unit-' + i"
[formControl]="unitGroup.controls[organizationUnits[i].displayName]"
/>
<label class="custom-control-label" [attr.for]="'unit-' + i">
<span [ngbTooltip]="getParentName(organizationUnits[i].parentId)">{{
organizationUnits[i].displayName
}}</span>
</label>
</div>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="nav" class="mt-2 fade-in-top"></div>
</form>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<abp-button iconClass="fa fa-check" (click)="save()" [disabled]="form?.invalid">{{
'AbpIdentity::Save' | abpLocalization
}}</abp-button>
</ng-template>
</abp-modal>
<abp-permission-management
*abpReplaceableTemplate="{
inputs: {
providerName: { value: 'U' },
providerKey: { value: providerKey },
hideBadges: { value: true },
visible: { value: visiblePermissions, twoWay: true }
},
outputs: { visibleChange: onVisiblePermissionChange },
componentKey: 'PermissionManagement.PermissionManagementComponent'
}"
[(visible)]="visiblePermissions"
[providerKey]="providerKey"
[hideBadges]="true"
providerName="U"
>
</abp-permission-management>
<abp-claim-modal [(visible)]="visibleClaims" [subject]="claimSubject"></abp-claim-modal>
<abp-modal [(visible)]="isSetPasswordModalVisible" [busy]="modalBusy" size="md">
<ng-template #abpHeader>
<h3>{{ 'AbpIdentity::SetPassword' | abpLocalization }}</h3>
</ng-template>
<ng-template #abpBody>
<form [formGroup]="setPasswordForm" (ngSubmit)="setPassword()" validateOnSubmit>
<div class="mt-2 fade-in-top">
<div class="form-group">
<label for="new-password">{{ 'AbpIdentity::Password' | abpLocalization }}</label>
<div class="input-group">
<div class="col-10 p-0">
<input
type="text"
id="new-password"
class="form-control"
formControlName="newPassword"
autofocus
/>
</div>
<div class="input-group-append col-2 p-0">
<button
class="btn btn-secondary mh-35"
id="generate-random-password-button"
type="button"
(click)="generatePassword()"
>
<i class="fa fa-refresh"></i>
</button>
</div>
</div>
</div>
</div>
</form>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<abp-button
iconClass="fa fa-check"
(click)="setPassword()"
[disabled]="setPasswordForm?.invalid"
>{{ 'AbpIdentity::Save' | abpLocalization }}</abp-button
>
</ng-template>
</abp-modal>
- Open
app.component.ts
insrc/app
folder and modify as shown below:
import { ReplaceableComponentsService } from '@abp/ng.core'; // added this line
import { Component } from '@angular/core';
import { UsersComponent } from './identity-extended/users.component'; // added this line
import { eIdentityComponents } from '@volo/abp.ng.identity'; // added this line
@Component({
selector: 'app-root',
template: `
<abp-loader-bar></abp-loader-bar>
<router-outlet></router-outlet>
`,
})
export class AppComponent {
constructor(private replaceableComponentsService: ReplaceableComponentsService) { // injected ReplaceableComponentsService
// added below
replaceableComponentsService.add({
component: UsersComponent,
key: eIdentityComponents.Users
});
}
}
Open users.component.html
and add disabled attribute to checkbox element like code below
<li id="user-roles" ngbNavItem>
<a ngbNavLink>{{ 'AbpIdentity::Roles' | abpLocalization }}</a>
<ng-template ngbNavContent>
<div
*ngFor="let roleGroup of roleGroups; let i = index; trackBy: trackByFn"
class="custom-checkbox custom-control mb-2"
>
<input
type="checkbox"
class="custom-control-input"
[attr.id]="'roles-' + i"
[formControl]="roleGroup.controls[roles[i].name]"
[disabled]="yourDisabledLogic" <!-- This line added -->
/>
<label class="custom-control-label" [attr.for]="'roles-' + i">{{
roles[i].name
}}</label>
</div></ng-template
>
</li>
Open users.component.html
and find li
element which id attribute's equal to user-organization-units
. You can remove this element or you can add ngIf directive to this element.
Any updates for version 5.2?