Repository navigation
Resource Based Permission - Angular UI Issue 24299 - #24459
Conversation
Introduces ResourcePermissionManagementComponent with UI and logic for managing resource-based permissions. Updates models and service to support resource permission APIs, and exports the new component in the index.
Moved resource-permission-management component files into a dedicated folder and updated imports. Replaced the manual table implementation with the ExtensibleTableComponent for resource permissions, added client-side pagination, and introduced extension points for entity property configuration. Added new defaults, services, and tokens to support extensibility and improved maintainability.
Added new translation keys for 'UpdatePermission', 'NoPermissionsAssigned', 'SelectProvider', 'SearchProviderKey', and 'Provider' across all supported languages in the permission management module. This enhances localization support for new UI features related to provider selection and permission assignment.
Added 'ErrorLoadingPermissions' and 'PleaseSelectProviderAndPermissions' keys to all language JSON files in the permission management module to support new error and prompt messages in the UI.
Enhances the provider key search input with focus/blur handling and a dropdown for results, improving user experience. Adds a confirmation dialog before deleting a permission, preventing accidental deletions. Refactors related logic for clarity and better state management.
| protected readonly confirmationService = inject(ConfirmationService); | ||
| readonly list = inject(ListService); | ||
|
|
||
| @Input() resourceName!: string; |
There was a problem hiding this comment.
It is better to use the latest input and output signal functions instead.
| } | ||
| } | ||
|
|
||
| toggleAllPermissions(selectAll: boolean) { |
There was a problem hiding this comment.
I think, we can improve the readability here like this
toggleAllPermissions(selectAll: boolean) {
let toggleAction = selectAll ? this.permissionsWithProvider().map(p => p.name || '') : [];
if (this.viewMode() === 'add') {
toggleAction = this.permissionDefinitions().map(p => p.name || '');
}
this.selectedPermissions.set(toggleAction);
}| } | ||
| </div> | ||
|
|
||
| <div class="position-relative mb-3"> |
There was a problem hiding this comment.
We can extract this and create generic component to behave like a lookup search component. You can check this as a reference https://github.com/volosoft/volo/tree/dev/abp/npm/ng-packs/packages/commercial-ui/src/lib/components/lookup
| </button> | ||
| </div> | ||
|
|
||
| <ng-template #actionsTemplate let-row> |
There was a problem hiding this comment.
These buttons does not seem configured to be a dropdown as an action button. We should try the bootstrap dropdown if possible.
Introduces ResourcePermissionStateService to centralize and manage state for resource permission management. Refactors resource-permission-management.component and related templates to use the new state service, and extracts form, list, and search UI into dedicated components for improved modularity and maintainability.
Moved inline templates to separate HTML files for permission-checkbox-list, provider-key-search, resource-permission-form, and resource-permission-list components. Updated component decorators to reference the new template files, improving maintainability and separation of concerns.
| this.searchSubject.pipe( | ||
| debounceTime(300), | ||
| distinctUntilChanged() | ||
| ).subscribe(filter => { |
There was a problem hiding this comment.
The subscription should be unsubscribed when the page is destroyed.
Introduces a generic LookupSearchComponent for reusable lookup/search UI in Angular. Refactors the provider key search in permission management to use the new component, simplifying its logic and improving maintainability. Updates tsconfig paths to include the new lookup component package.
| <abp-modal [(visible)]="visible" [busy]="modalBusy()" [options]="{ size: 'xl', scrollable: false }"> | ||
| <ng-template #abpHeader> | ||
| <h5 class="modal-title"> | ||
| @if (viewMode() === 'edit') { |
There was a problem hiding this comment.
Maybe we can define enum for ViewModes instead of using hardcoded string. Example;
export enum ViewModeValues {
List = 'list',
Add = 'add',
Edit = 'edit'
}
Replaced string literals for view modes with the new eResourcePermissionViewModes enum across components and services. This improves type safety and maintainability. Added the enum definition in a new file.
| this.searchSubject.pipe( | ||
| debounceTime(this.debounceTime()), | ||
| distinctUntilChanged(), | ||
| takeUntil(this.destroy$) |
There was a problem hiding this comment.
We can use takeUntilDestroyed instead. By this means, we will not need this un-subscription on destroy
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}The same applies for this component :: npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/provider-key-search/provider-key-search.component.ts
Here is a quick reference https://www.reddit.com/r/angular/comments/12vbfbd/angular_16_takeuntildestroyed_operator/
| ).subscribe({ | ||
| next: results => { | ||
| this.searchResults.set(results); | ||
| this.isLoading.set(false); |
There was a problem hiding this comment.
We can set the load false on finalize inside pipe to remove the duplication.
| private previousVisible = false; | ||
|
|
||
| constructor() { | ||
| effect(() => { |
There was a problem hiding this comment.
We can make this effect work more efficiently. Here is a quick example
effect(() => {
// Read all input values to track them
const resourceName = this.resourceName();
const resourceKey = this.resourceKey();
const resourceDisplayName = this.resourceDisplayName();
// Use untracked to prevent this effect from tracking writes to state signals
// This prevents circular dependencies and reduces unnecessary re-executions
untracked(() => {
// Only update if values have actually changed (signals optimize this internally)
this.state.resourceName.set(resourceName);
this.state.resourceKey.set(resourceKey);
this.state.resourceDisplayName.set(resourceDisplayName);
});
});|
|
||
| @if (showDropdown() && !disabled()) { | ||
| <div class="abp-lookup-dropdown list-group position-absolute w-100 shadow" | ||
| style="z-index: 1050; max-height: 200px; overflow-y: auto;"> |
There was a problem hiding this comment.
it's better to use class instead of inline styles
| <div class="border rounded p-3" style="max-height: 300px; overflow-y: auto;"> | ||
| @for (perm of permissions(); track perm.name) { | ||
| <div class="form-check"> | ||
| <input class="form-check-input" type="checkbox" [id]="'perm-' + idPrefix() + '-' + perm.name" |
There was a problem hiding this comment.
we can use string interpolation. it's more readable.
id="perm-{{ idPrefix() }}-{{ perm.name }}"
| {{ 'AbpPermissionManagement::GrantAllResourcePermissions' | abpLocalization }} | ||
| </label> | ||
| </div> | ||
| <div class="border rounded p-3" style="max-height: 300px; overflow-y: auto;"> |
There was a problem hiding this comment.
it's better to use class instead of inline styles
|
|
||
| readonly visible = model<boolean>(false); | ||
|
|
||
| private previousVisible = false; |
There was a problem hiding this comment.
we can use signal for previousVisible
| } | ||
|
|
||
| ngOnInit() { | ||
| this.list.maxResultCount = 10; |
There was a problem hiding this comment.
maybe we can define constant variable for maxResultCount instead of using hardcoded value
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.
Description
Resolves #24299 (write the related issue number if available)
How to test it?
you have to change test-branch-24299
you have to run dbmigrator and hostwithids on app templates
you have to run dev-app