diff --git a/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html b/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html
index fac156d548f..043e5dae378 100644
--- a/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html
+++ b/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html
@@ -42,7 +42,7 @@
@@ -79,6 +79,7 @@
+
@if (loadingError) {
{{ loadingError }}
diff --git a/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.ts b/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.ts
index 5aac6ccaa45..691e523d8bb 100644
--- a/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.ts
+++ b/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.ts
@@ -1,9 +1,9 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
- Component, input,
+ Component, computed, input,
OnChanges,
- OnInit, viewChild,
+ OnInit, Signal, viewChild,
} from '@angular/core';
import { ControlValueAccessor, NgControl, ReactiveFormsModule } from '@angular/forms';
import { MatButton } from '@angular/material/button';
@@ -16,7 +16,7 @@ import {
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { TranslateService, TranslateModule } from '@ngx-translate/core';
import { firstValueFrom, Observable, of } from 'rxjs';
-import { catchError } from 'rxjs/operators';
+import { catchError, map } from 'rxjs/operators';
import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive';
import { ExplorerNodeType } from 'app/enums/explorer-type.enum';
import { mntPath } from 'app/enums/mnt-path.enum';
@@ -63,6 +63,7 @@ import { ErrorHandlerService } from 'app/services/error-handler.service';
export class IxExplorerComponent implements OnInit, OnChanges, ControlValueAccessor {
readonly label = input();
readonly hint = input();
+ readonly readonly = input(false);
readonly multiple = input(false);
readonly tooltip = input();
readonly required = input(false);
@@ -110,13 +111,18 @@ export class IxExplorerComponent implements OnInit, OnChanges, ControlValueAcces
},
};
- treeOptions: ITreeOptions = {
- idField: 'path',
- displayField: 'name',
- getChildren: (node: TreeNode) => firstValueFrom(this.loadChildren(node)),
- actionMapping: this.actionMapping,
- useTriState: false,
- };
+ treeOptions: Signal = computed(() => {
+ const readonly = this.readonly();
+
+ return {
+ idField: 'path',
+ displayField: 'name',
+ getChildren: (node: TreeNode) => firstValueFrom(this.loadChildren(node)),
+ actionMapping: readonly ? {} : this.actionMapping,
+ useTriState: false,
+ useCheckbox: this.multiple(),
+ };
+ });
constructor(
public controlDirective: NgControl,
@@ -129,10 +135,6 @@ export class IxExplorerComponent implements OnInit, OnChanges, ControlValueAcces
}
ngOnChanges(changes: IxSimpleChanges): void {
- if ('multiple' in changes) {
- this.treeOptions.useCheckbox = this.multiple();
- }
-
if ('nodeProvider' in changes || 'root' in changes) {
this.setInitialNode();
this.cdr.markForCheck();
@@ -159,7 +161,7 @@ export class IxExplorerComponent implements OnInit, OnChanges, ControlValueAcces
}
setDisabledState?(isDisabled: boolean): void {
- this.isDisabled = isDisabled;
+ this.isDisabled = isDisabled || this.readonly();
this.cdr.markForCheck();
}
@@ -267,7 +269,7 @@ export class IxExplorerComponent implements OnInit, OnChanges, ControlValueAcces
{
path: this.root(),
name: this.root(),
- hasChildren: true,
+ hasChildren: !this.readonly(),
type: ExplorerNodeType.Directory,
isMountpoint: true,
},
@@ -279,6 +281,9 @@ export class IxExplorerComponent implements OnInit, OnChanges, ControlValueAcces
}
private selectTreeNodes(nodeIds: string[]): void {
+ if (this.readonly()) {
+ return;
+ }
const treeState = {
...this.tree().treeModel.getState(),
selectedLeafNodeIds: nodeIds.reduce((acc, nodeId) => ({ ...acc, [nodeId]: true }), {}),
@@ -288,6 +293,9 @@ export class IxExplorerComponent implements OnInit, OnChanges, ControlValueAcces
}
private loadChildren(node: TreeNode): Observable {
+ if (this.readonly()) {
+ return of([]);
+ }
this.loadingError = null;
this.cdr.markForCheck();
@@ -296,6 +304,10 @@ export class IxExplorerComponent implements OnInit, OnChanges, ControlValueAcces
}
return this.nodeProvider()(node).pipe(
+ map((childNodes) => childNodes.map((data) => {
+ data.hasChildren = !this.readonly() && data.hasChildren;
+ return data;
+ })),
catchError((error: unknown) => {
this.loadingError = this.errorHandler.getFirstErrorMessage(error);
this.cdr.markForCheck();
diff --git a/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.html b/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.html
index 0ac945a4e7f..a6f833eddcf 100644
--- a/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.html
+++ b/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.html
@@ -42,6 +42,7 @@
[label]="helptext.path_placeholder | translate"
[tooltip]="helptext.path_tooltip | translate"
[required]="true"
+ [readonly]="!hasRequiredRoles()"
[nodeProvider]="fileNodeProvider"
>
}
@@ -50,6 +51,7 @@
formControlName="path_source"
[label]="helptext.path_placeholder | translate"
[tooltip]="helptext.path_tooltip | translate"
+ [readonly]="!hasRequiredRoles()"
[required]="true"
[multiple]="true"
[nodeProvider]="fileNodeProvider"
diff --git a/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts b/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts
index 0174315dce6..b4d23f422d1 100644
--- a/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts
+++ b/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts
@@ -212,7 +212,7 @@ export class CloudSyncFormComponent implements OnInit {
bucketOptions$ = of([]);
- private hasRequiredRoles = toSignal(this.authService.hasRole(this.requiredRoles));
+ protected readonly hasRequiredRoles = toSignal(this.authService.hasRole(this.requiredRoles));
fileNodeProvider: TreeNodeProvider;
bucketNodeProvider: TreeNodeProvider;