From f74ca2fb971a3213dd6805dfd766c61dafc0c5f2 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 27 Mar 2025 11:16:06 +0200 Subject: [PATCH] Optimized entities map --- ...culated-field-arguments-table.component.ts | 65 +++++++++++++------ ...ulated-field-argument-panel.component.html | 1 + ...lculated-field-argument-panel.component.ts | 14 +++- .../shared/models/calculated-field.models.ts | 1 + 4 files changed, 57 insertions(+), 24 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts index 8f7e3b9893..02b258b90e 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts @@ -49,7 +49,7 @@ import { TbPopoverService } from '@shared/components/popover.service'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { EntityId } from '@shared/models/id/entity-id'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; -import { getEntityDetailsPageURL, isDefined, isDefinedAndNotNull, isEqual } from '@core/utils'; +import { getEntityDetailsPageURL, isEqual } from '@core/utils'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract'; import { EntityService } from '@core/http/entity.service'; @@ -57,9 +57,9 @@ import { MatSort } from '@angular/material/sort'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { catchError } from 'rxjs/operators'; -import { NEVER } from 'rxjs'; +import { forkJoin, Observable } from 'rxjs'; import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { BaseData } from '@shared/models/base-data'; @Component({ selector: 'tb-calculated-field-arguments-table', @@ -115,7 +115,6 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces private store: Store ) { this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => { - this.updateEntityNameMap(value); this.updateDataSource(value); this.propagateChange(this.getArgumentsObject(value)); }); @@ -180,8 +179,11 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces ctx, {}, {}, {}, true); - this.popoverComponent.tbComponentRef.instance.argumentsDataApplied.subscribe(value=> { + this.popoverComponent.tbComponentRef.instance.argumentsDataApplied.subscribe(({ entityName, ...value }) => { this.popoverComponent.hide(); + if (entityName) { + this.entityNameMap.set(value.refEntityId.id, entityName); + } if (isExists) { this.argumentsFormArray.at(index).setValue(value); } else { @@ -220,7 +222,8 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces writeValue(argumentsObj: Record): void { this.argumentsFormArray.clear(); - this.populateArgumentsFormArray(argumentsObj) + this.populateArgumentsFormArray(argumentsObj); + this.updateEntityNameMap(this.argumentsFormArray.value); } getEntityDetailsPageURL(id: string, type: EntityType): string { @@ -238,23 +241,43 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces this.argumentsFormArray.updateValueAndValidity(); } - private updateEntityNameMap(value: CalculatedFieldArgumentValue[]): void { - value.forEach(({ refEntityId = {}}) => { - if (refEntityId.id && !this.entityNameMap.has(refEntityId.id)) { + private updateEntityNameMap(values: CalculatedFieldArgumentValue[]): void { + const entitiesByType = values.reduce((acc, { refEntityId = {}}) => { + if (refEntityId.id && refEntityId.entityType !== ArgumentEntityType.Tenant) { const { id, entityType } = refEntityId as EntityId; - this.entityService.getEntity(entityType as EntityType, id, { ignoreLoading: true, ignoreErrors: true }) - .pipe( - catchError(() => { - const control = this.argumentsFormArray.controls.find(control => control.value.refEntityId?.id === id); - control.setValue({ ...control.value, refEntityId: { ...control.value.refEntityId, id: NULL_UUID } }); - - return NEVER; - }), - takeUntilDestroyed(this.destroyRef) - ) - .subscribe(entity => this.entityNameMap.set(id, entity.name)); + acc[entityType] = acc[entityType] ?? []; + acc[entityType].push(id); } - }); + return acc; + }, {} as Record); + const tasks = Object.entries(entitiesByType).map(([entityType, ids]) => + this.entityService.getEntities(entityType as EntityType, ids) + ); + if (!tasks.length) { + return; + } + this.fetchEntityNames(tasks, values); + } + + private fetchEntityNames(tasks: Observable[]>[], values: CalculatedFieldArgumentValue[]): void { + forkJoin(tasks as Observable[]>[]) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((result: Array>[]) => { + result.forEach((entities: BaseData[]) => entities.forEach((entity: BaseData) => this.entityNameMap.set(entity.id.id, entity.name))); + let updateTable = false; + values.forEach(({ refEntityId }) => { + if (refEntityId?.id && !this.entityNameMap.has(refEntityId.id) && refEntityId.entityType !== ArgumentEntityType.Tenant) { + updateTable = true; + const control = this.argumentsFormArray.controls.find(control => control.value.refEntityId?.id === refEntityId.id); + const value = control.value; + value.refEntityId.id = NULL_UUID; + control.setValue(value, { emitEvent: false }); + } + }); + if (updateTable) { + this.argumentsFormArray.updateValueAndValidity(); + } + }); } private getSortValue(argument: CalculatedFieldArgumentValue, column: string): string { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html index 15286af377..7521395cb4 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html @@ -88,6 +88,7 @@ [placeholder]="'action.set' | translate" [required]="true" [entityType]="ArgumentEntityTypeParamsMap.get(entityType).entityType" + (entityChanged)="entityNameSubject.next($event?.name)" /> } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.ts index c5a7407d58..2a64c90f20 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.ts @@ -36,7 +36,7 @@ import { EntityId } from '@shared/models/id/entity-id'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { EntityFilter } from '@shared/models/query/query.models'; import { AliasFilterType } from '@shared/models/alias.models'; -import { merge } from 'rxjs'; +import { BehaviorSubject, merge } from 'rxjs'; import { MINUTE } from '@shared/models/time/time.models'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { AppState } from '@core/core.state'; @@ -84,6 +84,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI argumentTypes: ArgumentType[]; entityFilter: EntityFilter; + entityNameSubject = new BehaviorSubject(null); readonly argumentEntityTypes = Object.values(ArgumentEntityType) as ArgumentEntityType[]; readonly ArgumentEntityTypeTranslations = ArgumentEntityTypeTranslations; @@ -151,6 +152,9 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI if (refEntityId.entityType === ArgumentEntityType.Tenant) { refEntityId.id = this.tenantId; } + if (refEntityId.entityType !== ArgumentEntityType.Current && refEntityId.entityType !== ArgumentEntityType.Tenant) { + value.entityName = this.entityNameSubject.value; + } if (value.defaultValue) { value.defaultValue = value.defaultValue.trim(); } @@ -211,12 +215,16 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI } private observeEntityTypeChanges(): void { - this.argumentFormGroup.get('refEntityId').get('entityType').valueChanges + this.refEntityIdFormGroup.get('entityType').valueChanges .pipe(distinctUntilChanged(), takeUntilDestroyed()) .subscribe(type => { this.argumentFormGroup.get('refEntityId').get('id').setValue(''); + const isEntityWithId = type !== ArgumentEntityType.Tenant && type !== ArgumentEntityType.Current; this.argumentFormGroup.get('refEntityId') - .get('id')[type === ArgumentEntityType.Tenant || type === ArgumentEntityType.Current ? 'disable' : 'enable'](); + .get('id')[isEntityWithId ? 'enable' : 'disable'](); + if (!isEntityWithId) { + this.entityNameSubject.next(null); + } if (!this.enableAttributeScopeSelection) { this.refEntityKeyFormGroup.get('scope').setValue(AttributeScope.SERVER_SCOPE); } diff --git a/ui-ngx/src/app/shared/models/calculated-field.models.ts b/ui-ngx/src/app/shared/models/calculated-field.models.ts index 383f7c7f72..9cc9a62da8 100644 --- a/ui-ngx/src/app/shared/models/calculated-field.models.ts +++ b/ui-ngx/src/app/shared/models/calculated-field.models.ts @@ -144,6 +144,7 @@ export interface RefEntityId { export interface CalculatedFieldArgumentValue extends CalculatedFieldArgument { argumentName: string; + entityName?: string; } export type CalculatedFieldTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: Record, closeAllOnSave?: boolean) => Observable;