diff --git a/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.html b/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.html index 8cd2058699..4980e78273 100644 --- a/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.html +++ b/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.html @@ -1,8 +1,8 @@ diff --git a/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.ts b/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.ts index 5c320a539b..f8c624ec9d 100644 --- a/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.ts +++ b/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.ts @@ -3,13 +3,14 @@ import { ChangeDetectorRef, Component, ContentChild, - EventEmitter, inject, - Input, OnInit, - Output, TemplateRef, ViewEncapsulation, + input, + output, + signal, + effect } from '@angular/core'; import { NgbDropdown, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap'; import { @@ -56,32 +57,66 @@ export class TreeComponent implements OnInit { private cdr = inject(ChangeDetectorRef); private disableTreeStyleLoading = inject(DISABLE_TREE_STYLE_LOADING_TOKEN, { optional: true }); - dropPosition: number; + dropPosition!: number; dropdowns = {} as { [key: string]: NgbDropdown }; - @ContentChild('menu') menu: TemplateRef; - @ContentChild(TreeNodeTemplateDirective) customNodeTemplate: TreeNodeTemplateDirective; - @ContentChild(ExpandedIconTemplateDirective) expandedIconTemplate: ExpandedIconTemplateDirective; - @Output() readonly checkedKeysChange = new EventEmitter(); - @Output() readonly expandedKeysChange = new EventEmitter(); - @Output() readonly selectedNodeChange = new EventEmitter(); - @Output() readonly dropOver = new EventEmitter(); - @Output() readonly nzExpandChange = new EventEmitter(); - @Input() noAnimation = true; - @Input() draggable: boolean; - @Input() checkable: boolean; - @Input() checkStrictly: boolean; - @Input() checkedKeys = []; - @Input() nodes = []; - @Input() expandedKeys: string[] = []; - @Input() selectedNode: any; - @Input() changeCheckboxWithNode: boolean; - @Input() isNodeSelected = node => this.selectedNode?.id === node.key; - @Input() beforeDrop = (event: NzFormatBeforeDropEvent) => { - this.dropPosition = event.pos; - return of(false); - }; + @ContentChild('menu') menu!: TemplateRef; + @ContentChild(TreeNodeTemplateDirective) customNodeTemplate!: TreeNodeTemplateDirective; + @ContentChild(ExpandedIconTemplateDirective) expandedIconTemplate!: ExpandedIconTemplateDirective; + + // Output signals + readonly checkedKeysChange = output(); + readonly expandedKeysChange = output(); + readonly selectedNodeChange = output(); + readonly dropOver = output(); + readonly nzExpandChange = output(); + + // Input signals + readonly noAnimation = input(true); + readonly draggable = input(undefined); + readonly checkable = input(undefined); + readonly checkStrictly = input(undefined); + readonly checkedKeysInput = input([], { alias: 'checkedKeys' }); + readonly nodesInput = input([], { alias: 'nodes' }); + readonly expandedKeysInput = input([], { alias: 'expandedKeys' }); + readonly selectedNodeInput = input(undefined, { alias: 'selectedNode' }); + readonly changeCheckboxWithNode = input(undefined); + readonly isNodeSelectedFn = input<(node: any) => boolean>( + (node) => this._selectedNode() === node.key, + { alias: 'isNodeSelected' } + ); + readonly beforeDropFn = input<(event: NzFormatBeforeDropEvent) => any>( + (event: NzFormatBeforeDropEvent) => { + this.dropPosition = event.pos; + return of(false); + }, + { alias: 'beforeDrop' } + ); + + // Internal signals for two-way binding + protected readonly _checkedKeys = signal([]); + protected readonly _expandedKeys = signal([]); + protected readonly _selectedNode = signal(undefined); + protected readonly _nodes = signal([]); + + // Getters for template access + get checkedKeys() { return this._checkedKeys(); } + get expandedKeys() { return this._expandedKeys(); } + get selectedNode() { return this._selectedNode(); } + get nodes() { return this._nodes(); } + get isNodeSelected() { return this.isNodeSelectedFn(); } + get beforeDrop() { return this.beforeDropFn(); } + + constructor() { + // Sync input signals to internal signals + effect(() => { + this._checkedKeys.set(this.checkedKeysInput()); + this._expandedKeys.set(this.expandedKeysInput()); + this._selectedNode.set(this.selectedNodeInput()); + this._nodes.set(this.nodesInput()); + }); + } ngOnInit() { this.loadStyle(); @@ -97,13 +132,13 @@ export class TreeComponent implements OnInit { this.subscriptionService.addOne(loaded$); } - private findNode(target: any, nodes: any[]) { + private findNode(target: any, nodes: any[]): any { for (const node of nodes) { if (node.key === target.id) { return node; } if (node.children) { - const res = this.findNode(target, node.children); + const res: any = this.findNode(target, node.children); if (res) { return res; } @@ -113,36 +148,36 @@ export class TreeComponent implements OnInit { } onSelectedNodeChange(node: NzTreeNode) { - this.selectedNode = node.origin.entity; - if (this.changeCheckboxWithNode) { + this._selectedNode.set(node.origin.entity); + if (this.changeCheckboxWithNode()) { let newVal; if (node.isChecked) { - newVal = this.checkedKeys.filter(x => x !== node.key); + newVal = this._checkedKeys().filter(x => x !== node.key); } else { - newVal = [...this.checkedKeys, node.key]; + newVal = [...this._checkedKeys(), node.key]; } this.selectedNodeChange.emit(node); - this.checkedKeys = newVal; + this._checkedKeys.set(newVal); this.checkedKeysChange.emit(newVal); } else { this.selectedNodeChange.emit(node.origin.entity); } } - onCheckboxChange(event) { - this.checkedKeys = [...event.keys]; + onCheckboxChange(event: { keys: any[] }) { + this._checkedKeys.set([...event.keys]); this.checkedKeysChange.emit(event.keys); } - onExpandedKeysChange(event) { - this.expandedKeys = [...event.keys]; + onExpandedKeysChange(event: { keys: string[] } & NzFormatEmitEvent) { + this._expandedKeys.set([...event.keys]); this.expandedKeysChange.emit(event.keys); this.nzExpandChange.emit(event); } onDrop(event: DropEvent) { - event.event.stopPropagation(); - event.event.preventDefault(); + event.event?.stopPropagation(); + event.event?.preventDefault(); event.pos = this.dropPosition; this.dropOver.emit(event); @@ -160,12 +195,14 @@ export class TreeComponent implements OnInit { dropdown.close(); } }); - this.dropdowns[dropdownKey]?.toggle(); + if (dropdownKey) { + this.dropdowns[dropdownKey]?.toggle(); + } } setSelectedNode(node: any) { - const newSelectedNode = this.findNode(node, this.nodes); - this.selectedNode = { ...newSelectedNode }; + const newSelectedNode = this.findNode(node, this._nodes()); + this._selectedNode.set({ ...newSelectedNode }); this.cdr.markForCheck(); } } diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html index f59a66b6a5..bd08cbb7b7 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html @@ -3,8 +3,8 @@

{{ 'AbpFeatureManagement::Features' | abpLocalization }} - @if (providerTitle) { - - {{ providerTitle }} + @if (providerTitle()) { + - {{ providerTitle() }} }

@@ -38,7 +38,7 @@ @for (feature of features[group.name]; track feature.id || i; let i = $index) { @let provider = feature.provider.name; @let isFeatureDisabled = !feature.parentName ? isParentDisabled(feature.name, group.name, provider) : - (provider !== providerName && provider !== defaultProviderName); + (provider !== providerName() && provider !== defaultProviderName);
@switch (feature.valueType?.name) { diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts index 5feac210e5..47bfc3e96e 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output, inject, DOCUMENT } from '@angular/core'; +import { Component, inject, DOCUMENT, input, output, signal, effect } from '@angular/core'; import { NgTemplateOutlet } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { ConfigStateService, LocalizationPipe, TrackByService } from '@abp/ng.core'; @@ -61,14 +61,17 @@ export class FeatureManagementComponent protected readonly confirmationService = inject(ConfirmationService); private document = inject(DOCUMENT); - @Input() - providerKey: string; + // Signal inputs + readonly providerKey = input(undefined); + readonly providerName = input(undefined); + readonly providerTitle = input(undefined); + readonly visibleInput = input(false, { alias: 'visible' }); - @Input() - providerName: string; + // Output signals + readonly visibleChange = output(); - @Input({ required: false }) - providerTitle: string; + // Internal state + protected readonly _visible = signal(false); selectedGroupDisplayName: string; @@ -82,33 +85,41 @@ export class FeatureManagementComponent defaultProviderName = DEFAULT_PROVIDER_NAME; - protected _visible; + modalBusy = false; - @Input() + // Getter/setter for backward compatibility get visible(): boolean { - return this._visible; + return this._visible(); } set visible(value: boolean) { - if (this._visible === value) { + if (this._visible() === value) { return; } - this._visible = value; + this._visible.set(value); this.visibleChange.emit(value); if (value) { this.openModal(); - return; } } - @Output() readonly visibleChange = new EventEmitter(); - - modalBusy = false; + constructor() { + // Sync visible input to internal signal + effect(() => { + const inputValue = this.visibleInput(); + if (this._visible() !== inputValue) { + this._visible.set(inputValue); + if (inputValue) { + this.openModal(); + } + } + }); + } openModal() { - if (!this.providerName) { + if (!this.providerName()) { throw new Error('providerName is required.'); } @@ -116,7 +127,7 @@ export class FeatureManagementComponent } getFeatures() { - this.service.get(this.providerName, this.providerKey).subscribe(res => { + this.service.get(this.providerName()!, this.providerKey()).subscribe(res => { if (!res.groups?.length) return; this.groups = res.groups.map(({ name, displayName }) => ({ name, displayName })); this.selectedGroupDisplayName = this.groups[0].displayName; @@ -149,13 +160,13 @@ export class FeatureManagementComponent this.modalBusy = true; this.service - .update(this.providerName, this.providerKey, { features: changedFeatures }) + .update(this.providerName()!, this.providerKey(), { features: changedFeatures }) .pipe(finalize(() => (this.modalBusy = false))) .subscribe(() => { this.visible = false; this.toasterService.success('AbpUi::SavedSuccessfully'); - if (!this.providerKey) { + if (!this.providerKey()) { // to refresh host's features this.configState.refreshAppState().subscribe(); } @@ -167,11 +178,11 @@ export class FeatureManagementComponent .warn('AbpFeatureManagement::AreYouSureToResetToDefault', 'AbpFeatureManagement::AreYouSure') .subscribe((status: Confirmation.Status) => { if (status === Confirmation.Status.confirm) { - this.service.delete(this.providerName, this.providerKey).subscribe(() => { + this.service.delete(this.providerName()!, this.providerKey()).subscribe(() => { this.toasterService.success('AbpFeatureManagement::ResetedToDefault'); this.visible = false; - if (!this.providerKey) { + if (!this.providerKey()) { // to refresh host's features this.configState.refreshAppState().subscribe(); } @@ -190,17 +201,18 @@ export class FeatureManagementComponent isParentDisabled(parentName: string, groupName: string, provider: string): boolean { const children = this.features[groupName]?.filter(f => f.parentName === parentName); + const providerNameValue = this.providerName(); if (children?.length) { return children.some(child => { const childProvider = child.provider?.name; return ( - (childProvider !== this.providerName && childProvider !== this.defaultProviderName) || - (provider !== this.providerName && provider !== this.defaultProviderName) + (childProvider !== providerNameValue && childProvider !== this.defaultProviderName) || + (provider !== providerNameValue && provider !== this.defaultProviderName) ); }); } else { - return provider !== this.providerName && provider !== this.defaultProviderName; + return provider !== providerNameValue && provider !== this.defaultProviderName; } } diff --git a/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts b/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts index c04d917515..31a7efd22c 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts @@ -1,13 +1,14 @@ -import { EventEmitter } from '@angular/core'; +import { InputSignal, OutputEmitterRef } from '@angular/core'; export namespace FeatureManagement { export interface FeatureManagementComponentInputs { visible: boolean; - readonly providerName: string; - readonly providerKey: string; + readonly providerName: InputSignal; + readonly providerKey: InputSignal; } export interface FeatureManagementComponentOutputs { - readonly visibleChange: EventEmitter; + readonly visibleChange: OutputEmitterRef; } } + diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html b/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html index 0224f3d45a..0711bb995b 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html +++ b/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html @@ -1,9 +1,9 @@ - @if (data.entityDisplayName || entityDisplayName) { + @if (data.entityDisplayName || entityDisplayName()) {

{{ 'AbpPermissionManagement::Permissions' | abpLocalization }} - - {{ entityDisplayName || data.entityDisplayName }} + {{ entityDisplayName() || data.entityDisplayName }}

@@ -115,7 +115,7 @@ />