Skip to content

Commit faf4ea1

Browse files
committed
Refactor lookup and permission components, add styles
Refactored lookup-search and permission-checkbox-list components to use SCSS for dropdown and list container styling. Improved resource permission management component with signals and untracked for state updates, and replaced manual destroy logic with Angular's DestroyRef and takeUntilDestroyed. Updated HTML templates for better readability and accessibility.
1 parent 4fe44e8 commit faf4ea1

File tree

8 files changed

+83
-89
lines changed

8 files changed

+83
-89
lines changed
Lines changed: 32 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,45 @@
11
<div class="abp-lookup-container position-relative">
22
@if (label()) {
3-
<label class="form-label">{{ label() | abpLocalization }}</label>
3+
<label class="form-label">{{ label() | abpLocalization }}</label>
44
}
55

66
<div class="input-group">
7-
<input
8-
type="text"
9-
class="form-control"
10-
[placeholder]="placeholder() | abpLocalization"
11-
[ngModel]="displayValue()"
12-
(ngModelChange)="onSearchInput($event)"
13-
(focus)="onSearchFocus()"
14-
(blur)="onSearchBlur($event)"
15-
[disabled]="disabled()"
16-
/>
7+
<input type="text" class="form-control" [placeholder]="placeholder() | abpLocalization" [ngModel]="displayValue()"
8+
(ngModelChange)="onSearchInput($event)" (focus)="onSearchFocus()" (blur)="onSearchBlur($event)"
9+
[disabled]="disabled()" />
1710
@if (displayValue() && !disabled()) {
18-
<button
19-
type="button"
20-
class="btn btn-outline-secondary"
21-
(mousedown)="clearSelection()"
22-
tabindex="-1"
23-
>
24-
<i class="fa fa-times"></i>
25-
</button>
11+
<button type="button" class="btn btn-outline-secondary" (mousedown)="clearSelection()" tabindex="-1">
12+
<i class="fa fa-times"></i>
13+
</button>
2614
}
2715
</div>
2816

2917
@if (showDropdown() && !disabled()) {
30-
<div
31-
class="abp-lookup-dropdown list-group position-absolute w-100 shadow"
32-
style="z-index: 1050; max-height: 200px; overflow-y: auto"
33-
>
34-
@if (isLoading()) {
35-
<div class="list-group-item text-center py-3">
36-
<i class="fa fa-spinner fa-spin me-2"></i>
37-
{{ 'AbpUi::Loading' | abpLocalization }}
38-
</div>
39-
} @else if (searchResults().length > 0) {
40-
@for (item of searchResults(); track item.key) {
41-
<button
42-
type="button"
43-
class="list-group-item list-group-item-action"
44-
(mousedown)="selectItem(item)"
45-
>
46-
@if (itemTemplate()) {
47-
<ng-container *ngTemplateOutlet="itemTemplate()!; context: { $implicit: item }" />
48-
} @else {
49-
{{ getDisplayValue(item) }}
50-
}
51-
</button>
52-
}
53-
} @else if (displayValue()) {
54-
@if (noResultsTemplate()) {
55-
<ng-container *ngTemplateOutlet="noResultsTemplate()!" />
56-
} @else {
57-
<div class="list-group-item text-muted">
58-
{{ 'AbpUi::NoRecordsFound' | abpLocalization }}
59-
</div>
60-
}
18+
<div class="abp-lookup-dropdown list-group position-absolute w-100 shadow">
19+
@if (isLoading()) {
20+
<div class="list-group-item text-center py-3">
21+
<i class="fa fa-spinner fa-spin me-2"></i>
22+
{{ 'AbpUi::Loading' | abpLocalization }}
23+
</div>
24+
} @else if (searchResults().length > 0) {
25+
@for (item of searchResults(); track item.key) {
26+
<button type="button" class="list-group-item list-group-item-action" (mousedown)="selectItem(item)">
27+
@if (itemTemplate()) {
28+
<ng-container *ngTemplateOutlet="itemTemplate()!; context: { $implicit: item }" />
29+
} @else {
30+
{{ getDisplayValue(item) }}
6131
}
32+
</button>
33+
}
34+
} @else if (displayValue()) {
35+
@if (noResultsTemplate()) {
36+
<ng-container *ngTemplateOutlet="noResultsTemplate()!" />
37+
} @else {
38+
<div class="list-group-item text-muted">
39+
{{ 'AbpUi::NoRecordsFound' | abpLocalization }}
6240
</div>
41+
}
42+
}
43+
</div>
6344
}
64-
</div>
45+
</div>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.abp-lookup-dropdown {
2+
z-index: 1050;
3+
max-height: 200px;
4+
overflow-y: auto;
5+
}

npm/ng-packs/packages/components/lookup/src/lib/lookup-search.component.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ import {
55
model,
66
signal,
77
OnInit,
8-
OnDestroy,
98
ChangeDetectionStrategy,
109
TemplateRef,
1110
contentChild,
11+
DestroyRef,
12+
inject,
1213
} from '@angular/core';
14+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
1315
import { CommonModule } from '@angular/common';
1416
import { FormsModule } from '@angular/forms';
1517
import { LocalizationPipe } from '@abp/ng.core';
16-
import { Subject, Observable, debounceTime, distinctUntilChanged, takeUntil, of } from 'rxjs';
18+
import { Subject, Observable, debounceTime, distinctUntilChanged, of, finalize } from 'rxjs';
1719

1820
export interface LookupItem {
1921
key: string;
@@ -26,10 +28,12 @@ export type LookupSearchFn<T = LookupItem> = (filter: string) => Observable<T[]>
2628
@Component({
2729
selector: 'abp-lookup-search',
2830
templateUrl: './lookup-search.component.html',
31+
styleUrl: './lookup-search.component.scss',
2932
imports: [CommonModule, FormsModule, LocalizationPipe],
3033
changeDetection: ChangeDetectionStrategy.OnPush,
3134
})
32-
export class LookupSearchComponent<T extends LookupItem = LookupItem> implements OnInit, OnDestroy {
35+
export class LookupSearchComponent<T extends LookupItem = LookupItem> implements OnInit {
36+
private readonly destroyRef = inject(DestroyRef);
3337

3438
readonly label = input<string>();
3539
readonly placeholder = input<string>('');
@@ -55,23 +59,17 @@ export class LookupSearchComponent<T extends LookupItem = LookupItem> implements
5559
readonly isLoading = signal(false);
5660

5761
private readonly searchSubject = new Subject<string>();
58-
private readonly destroy$ = new Subject<void>();
5962

6063
ngOnInit() {
6164
this.searchSubject.pipe(
6265
debounceTime(this.debounceTime()),
6366
distinctUntilChanged(),
64-
takeUntil(this.destroy$)
67+
takeUntilDestroyed(this.destroyRef)
6568
).subscribe(filter => {
6669
this.performSearch(filter);
6770
});
6871
}
6972

70-
ngOnDestroy() {
71-
this.destroy$.next();
72-
this.destroy$.complete();
73-
}
74-
7573
onSearchInput(filter: string) {
7674
this.displayValue.set(filter);
7775
this.showDropdown.set(true);
@@ -120,15 +118,14 @@ export class LookupSearchComponent<T extends LookupItem = LookupItem> implements
120118
this.isLoading.set(true);
121119

122120
this.searchFn()(filter).pipe(
123-
takeUntil(this.destroy$)
121+
takeUntilDestroyed(this.destroyRef),
122+
finalize(() => this.isLoading.set(false))
124123
).subscribe({
125124
next: results => {
126125
this.searchResults.set(results);
127-
this.isLoading.set(false);
128126
},
129127
error: () => {
130128
this.searchResults.set([]);
131-
this.isLoading.set(false);
132129
}
133130
});
134131
}

npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
<h5>{{ title() | abpLocalization }}</h5>
44
}
55
<div class="form-check form-switch mb-2">
6-
<input class="form-check-input" type="checkbox" [id]="'grantAll-' + idPrefix()"
6+
<input class="form-check-input" type="checkbox" id="grantAll-{{ idPrefix() }}"
77
[checked]="state.allPermissionsSelected()"
88
(change)="state.toggleAllPermissions(!state.allPermissionsSelected())" />
9-
<label class="form-check-label" [for]="'grantAll-' + idPrefix()">
9+
<label class="form-check-label" for="grantAll-{{ idPrefix() }}">
1010
{{ 'AbpPermissionManagement::GrantAllResourcePermissions' | abpLocalization }}
1111
</label>
1212
</div>
13-
<div class="border rounded p-3" style="max-height: 300px; overflow-y: auto;">
13+
<div class="abp-permission-list-container border rounded p-3">
1414
@for (perm of permissions(); track perm.name) {
1515
<div class="form-check">
16-
<input class="form-check-input" type="checkbox" [id]="'perm-' + idPrefix() + '-' + perm.name"
16+
<input class="form-check-input" type="checkbox" id="perm-{{ idPrefix() }}-{{ perm.name }}"
1717
[checked]="state.isPermissionSelected(perm.name || '')"
1818
(change)="state.togglePermission(perm.name || '')" />
19-
<label class="form-check-label" [for]="'perm-' + idPrefix() + '-' + perm.name">
19+
<label class="form-check-label" for="perm-{{ idPrefix() }}-{{ perm.name }}">
2020
{{ perm.displayName }}
2121
</label>
2222
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.abp-permission-list-container {
2+
max-height: 300px;
3+
overflow-y: auto;
4+
}

npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface PermissionItem {
1010
@Component({
1111
selector: 'abp-permission-checkbox-list',
1212
templateUrl: './permission-checkbox-list.component.html',
13+
styleUrl: './permission-checkbox-list.component.scss',
1314
imports: [LocalizationPipe],
1415
changeDetection: ChangeDetectionStrategy.OnPush,
1516
})

npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/provider-key-search/provider-key-search.component.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { Component, input, inject, OnInit, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
1+
import { Component, input, inject, OnInit, ChangeDetectionStrategy, DestroyRef } from '@angular/core';
2+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
23
import { PermissionsService } from '@abp/ng.permission-management/proxy';
34
import { LookupSearchComponent, LookupItem } from '@abp/ng.components/lookup';
4-
import { Observable, map, Subject, takeUntil } from 'rxjs';
5+
import { Observable, map } from 'rxjs';
56
import { ResourcePermissionStateService } from '../../../services/resource-permission-state.service';
67

78
interface ProviderKeyLookupItem extends LookupItem {
@@ -15,25 +16,19 @@ interface ProviderKeyLookupItem extends LookupItem {
1516
imports: [LookupSearchComponent],
1617
changeDetection: ChangeDetectionStrategy.OnPush,
1718
})
18-
export class ProviderKeySearchComponent implements OnInit, OnDestroy {
19+
export class ProviderKeySearchComponent implements OnInit {
1920
readonly state = inject(ResourcePermissionStateService);
2021
private readonly service = inject(PermissionsService);
22+
private readonly destroyRef = inject(DestroyRef);
2123

2224
readonly resourceName = input.required<string>();
2325

24-
private readonly destroy$ = new Subject<void>();
25-
2626
searchFn: (filter: string) => Observable<ProviderKeyLookupItem[]> = () => new Observable();
2727

2828
ngOnInit() {
2929
this.searchFn = (filter: string) => this.loadProviderKeys(filter);
3030
}
3131

32-
ngOnDestroy() {
33-
this.destroy$.next();
34-
this.destroy$.complete();
35-
}
36-
3732
onItemSelected(item: ProviderKeyLookupItem) {
3833
// State is already updated via displayValue and selectedValue bindings
3934
// This handler can be used for additional side effects if needed
@@ -60,7 +55,7 @@ export class ProviderKeySearchComponent implements OnInit, OnDestroy {
6055
providerKey: k.providerKey || '',
6156
providerDisplayName: k.providerDisplayName || undefined,
6257
}))),
63-
takeUntil(this.destroy$)
58+
takeUntilDestroyed(this.destroyRef)
6459
);
6560
}
6661
}

npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
model,
1919
OnInit,
2020
effect,
21+
untracked,
22+
signal,
2123
} from '@angular/core';
2224
import { finalize, switchMap, of } from 'rxjs';
2325
import { ResourcePermissionStateService } from '../../services/resource-permission-state.service';
@@ -26,6 +28,8 @@ import { ResourcePermissionFormComponent } from './resource-permission-form/reso
2628

2729
import { eResourcePermissionViewModes } from '../../enums/view-modes';
2830

31+
const DEFAULT_MAX_RESULT_COUNT = 10;
32+
2933
@Component({
3034
selector: 'abp-resource-permission-management',
3135
templateUrl: './resource-permission-management.component.html',
@@ -55,33 +59,40 @@ export class ResourcePermissionManagementComponent implements OnInit {
5559

5660
readonly visible = model<boolean>(false);
5761

58-
private previousVisible = false;
62+
private readonly previousVisible = signal(false);
5963

6064
constructor() {
6165
effect(() => {
62-
this.state.resourceName.set(this.resourceName());
63-
this.state.resourceKey.set(this.resourceKey());
64-
this.state.resourceDisplayName.set(this.resourceDisplayName());
66+
const resourceName = this.resourceName();
67+
const resourceKey = this.resourceKey();
68+
const resourceDisplayName = this.resourceDisplayName();
69+
70+
untracked(() => {
71+
this.state.resourceName.set(resourceName);
72+
this.state.resourceKey.set(resourceKey);
73+
this.state.resourceDisplayName.set(resourceDisplayName);
74+
});
6575
});
6676

6777
effect(() => {
6878
const isVisible = this.visible();
69-
if (isVisible && !this.previousVisible) {
79+
const wasVisible = this.previousVisible();
80+
if (isVisible && !wasVisible) {
7081
this.openModal();
71-
} else if (!isVisible && this.previousVisible) {
82+
} else if (!isVisible && wasVisible) {
7283
this.state.reset();
7384
}
74-
this.previousVisible = isVisible;
85+
untracked(() => this.previousVisible.set(isVisible));
7586
});
7687
}
7788

7889
ngOnInit() {
79-
this.list.maxResultCount = 10;
90+
this.list.maxResultCount = DEFAULT_MAX_RESULT_COUNT;
8091

8192
this.list.hookToQuery(query => {
8293
const allData = this.state.allResourcePermissions();
8394
const skipCount = query.skipCount || 0;
84-
const maxResultCount = query.maxResultCount || 10;
95+
const maxResultCount = query.maxResultCount || DEFAULT_MAX_RESULT_COUNT;
8596

8697
const paginatedData = allData.slice(skipCount, skipCount + maxResultCount);
8798

0 commit comments

Comments
 (0)