From ce3b2579421e6dad5418f19c087e4e036ef7c03b Mon Sep 17 00:00:00 2001 From: Maksym Tsymbarov Date: Tue, 9 Dec 2025 12:05:11 +0200 Subject: [PATCH] clean up and propagation of limits values --- .../widget/lib/chart/time-series-chart.ts | 27 ++++---- .../common/axis-scale-row.component.scss | 15 ----- .../common/axis-scale-row.component.ts | 67 +++++-------------- ...me-series-chart-axis-settings.component.ts | 16 +---- ...ime-series-chart-y-axes-panel.component.ts | 31 +++++---- ...ime-series-chart-y-axis-row.component.html | 10 +-- .../time-series-chart-y-axis-row.component.ts | 24 ++++++- 7 files changed, 75 insertions(+), 115 deletions(-) delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.scss diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index d429438f43..8568e4b13b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -594,10 +594,10 @@ export class TbTimeSeriesChart { } const yAxis = createTimeSeriesYAxis(unitSymbol, decimals, axisSettings, this.ctx.utilsService, this.darkMode, unitConvertor); if (isDefinedAndNotNull(axisSettings.min)) { - this.processYAxisLimit(axisSettings.min, 'min', yAxis, axisLimitDatasources, unitConvertor); + this.processYAxisLimit(axisSettings.min as ValueSourceConfig, 'min', yAxis, axisLimitDatasources, unitConvertor); } if (isDefinedAndNotNull(axisSettings.max)) { - this.processYAxisLimit(axisSettings.max, 'max', yAxis, axisLimitDatasources, unitConvertor); + this.processYAxisLimit(axisSettings.max as ValueSourceConfig, 'max', yAxis, axisLimitDatasources, unitConvertor); } this.yAxisList.push(yAxis); } @@ -605,22 +605,21 @@ export class TbTimeSeriesChart { } private processYAxisLimit( - limit: any, + limit: ValueSourceConfig, limitType: 'min' | 'max', yAxis: TimeSeriesChartYAxis, axisLimitDatasources: Datasource[], unitConvertor?: (value: number) => number ): void { if (limit && typeof limit === 'object' && 'type' in limit) { - const axisLimit = limit as ValueSourceConfig; - if (axisLimit.type === ValueSourceType.latestKey) { + if (limit.type === ValueSourceType.latestKey) { let latestDataKey: DataKey = null; if (this.ctx.datasources.length) { for (const datasource of this.ctx.datasources) { latestDataKey = datasource.latestDataKeys?.find(d => - (d.type === DataKeyType.function && d.label === axisLimit.latestKey) || - (d.type !== DataKeyType.function && d.name === axisLimit.latestKey && - d.type === axisLimit.latestKeyType)); + (d.type === DataKeyType.function && d.label === limit.latestKey) || + (d.type !== DataKeyType.function && d.name === limit.latestKey && + d.type === limit.latestKeyType)); if (latestDataKey) { break; } @@ -633,8 +632,8 @@ export class TbTimeSeriesChart { yAxis.maxLatestDataKey = latestDataKey; } } - } else if (axisLimit.type === ValueSourceType.entity) { - const entityAliasId = this.ctx.aliasController.getEntityAliasId(axisLimit.entityAlias); + } else if (limit.type === ValueSourceType.entity) { + const entityAliasId = this.ctx.aliasController.getEntityAliasId(limit.entityAlias); if (entityAliasId) { let datasource = axisLimitDatasources.find(d => d.entityAliasId === entityAliasId); const entityDataKey: DataKey = { @@ -651,16 +650,16 @@ export class TbTimeSeriesChart { } else { datasource = { type: DatasourceType.entity, - name: axisLimit.entityAlias, - aliasName: axisLimit.entityAlias, + name: limit.entityAlias, + aliasName: limit.entityAlias, entityAliasId, dataKeys: [entityDataKey] }; axisLimitDatasources.push(datasource); } } - } else if (axisLimit.type === ValueSourceType.constant) { - const value = unitConvertor ? unitConvertor(axisLimit.value) : axisLimit.value; + } else if (limit.type === ValueSourceType.constant) { + const value = unitConvertor ? unitConvertor(limit.value) : limit.value; if (limitType === 'min') { yAxis.option.min = value; } else { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.scss deleted file mode 100644 index 1a4214be18..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.scss +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright © 2016-2025 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts index 9962bdeed9..f01336d22e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts @@ -38,7 +38,6 @@ import { merge } from 'rxjs'; @Component({ selector: 'tb-axis-scale-row', templateUrl: './axis-scale-row.component.html', - styleUrl: './axis-scale-row.component.scss', providers: [ { provide: NG_VALUE_ACCESSOR, @@ -101,7 +100,6 @@ export class AxisScaleRowComponent implements ControlValueAccessor, OnInit, Vali }); this.latestKeyFormControl = this.fb.control(null, [Validators.required]); this.entityKeyFormControl = this.fb.control(null, [Validators.required]); - this.subscribeToTypeChanges(); merge( this.latestKeyFormControl.valueChanges, this.entityKeyFormControl.valueChanges, @@ -150,43 +148,25 @@ export class AxisScaleRowComponent implements ControlValueAccessor, OnInit, Vali const errors: any = {}; if (this.limitForm.invalid) { - errors.form = this.getFormErrors(); + errors.form = false; } if (type === ValueSourceType.latestKey) { if (!this.latestKeyFormControl.value || this.latestKeyFormControl.invalid) { - errors.latestKey = { - valid: false - }; + errors.latestKey = false; } } else if (type === ValueSourceType.entity) { if (!this.limitForm.get('entityAlias')?.value) { - errors.entityAlias = { - valid: false - }; + errors.entityAlias = false; } if (!this.entityKeyFormControl.value || this.entityKeyFormControl.invalid) { - errors.entityKey = { - valid: false - }; + errors.entityKey = false; } } return Object.keys(errors).length ? { axisLimitForm: errors } : null; } - - private getFormErrors(): any { - const errors: any = {}; - Object.keys(this.limitForm.controls).forEach(key => { - const control = this.limitForm.get(key); - if (control && control.errors) { - errors[key] = control.errors; - } - }); - return errors; - } - private updateValidators() { const axisTypeControl = this.limitForm.get('type'); if (axisTypeControl && this.entityKeyFormControl && this.latestKeyFormControl) { @@ -207,38 +187,23 @@ export class AxisScaleRowComponent implements ControlValueAccessor, OnInit, Vali } } - private subscribeToTypeChanges() { - this.limitForm.controls.type.valueChanges - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(() => { - const axisValueControl = this.limitForm.get('value'); - const axisEntityAliasControl = this.limitForm.get('entityAlias'); - if (axisValueControl) { - axisValueControl.setValue(null, { emitEvent: false }); - } - if (axisEntityAliasControl) { - axisEntityAliasControl.setValue(null, { emitEvent: false }); - } - this.latestKeyFormControl.setValue(null, { emitEvent: false }); - this.entityKeyFormControl.setValue(null, { emitEvent: false }); - - }); - } - private updateModel() { const value = this.limitForm.value; - this.modelValue.type = value.type ?? ValueSourceType.constant; - this.modelValue.value = value?.value; - this.modelValue.entityAlias = value?.entityAlias; - if (value.type === ValueSourceType.latestKey) { + const type = value.type; + let updates: Partial = { type }; + if (type === ValueSourceType.latestKey) { const latestKey: DataKey = this.latestKeyFormControl.value; - this.modelValue.latestKey = latestKey?.name; - this.modelValue.latestKeyType = (latestKey?.type as any); - } else if (value.type === ValueSourceType.entity) { + updates.latestKey = latestKey?.name; + updates.latestKeyType = latestKey?.type as any; + } else if (type === ValueSourceType.entity) { const entityKey: DataKey = this.entityKeyFormControl.value; - this.modelValue.entityKey = entityKey?.name; - this.modelValue.entityKeyType = (entityKey?.type as any); + updates.entityKey = entityKey?.name; + updates.entityKeyType = entityKey?.type as any; + updates.entityAlias = value?.entityAlias; + } else { + updates.value = value?.value; } + this.modelValue = updates as ValueSourceConfig; this.propagateChanges(this.modelValue); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts index 4bf7a78f98..9bed5d6e7c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts @@ -168,10 +168,7 @@ export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValu validate(): ValidationErrors | null { return this.axisSettingsFormGroup.valid ? null : { - axisSettings: { - valid: false, - errors: this.getFormErrors() - } + axisSettings: false }; } @@ -250,17 +247,6 @@ export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValu } } - private getFormErrors(): any { - const errors: any = {}; - Object.keys(this.axisSettingsFormGroup.controls).forEach(key => { - const control = this.axisSettingsFormGroup.get(key); - if (control && control.errors) { - errors[key] = control.errors; - } - }); - return errors; - } - private updateModel() { this.modelValue = this.axisSettingsFormGroup.getRawValue(); this.propagateChange(this.modelValue); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts index 3159a27abd..4d710a6ff4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts @@ -196,6 +196,8 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, const axes: TimeSeriesChartYAxisSettings[] = this.yAxesFormGroup.get('axes').value; axis.id = getNextTimeSeriesYAxisId(axes); axis.order = axes.length; + axis.min = this.normalizeAxisLimit(axis.min); + axis.max = this.normalizeAxisLimit(axis.max); const axesArray = this.yAxesFormGroup.get('axes') as UntypedFormArray; const axisControl = this.fb.control(axis, [timeSeriesChartYAxisValidator]); axesArray.push(axisControl); @@ -204,8 +206,6 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, private prepareAxesFormArray(axes: TimeSeriesChartYAxisSettings[]): UntypedFormArray { const axesControls: Array = []; axes.forEach((axis) => { - axis.min = this.normalizeAxisLimit(axis.min); - axis.max = this.normalizeAxisLimit(axis.max); axesControls.push(this.fb.control(axis, [timeSeriesChartYAxisValidator])); }); return this.fb.array(axesControls); @@ -216,17 +216,20 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, const result: TimeSeriesChartYAxes = {}; for (const [id, axis] of Object.entries(yAxes)) { + axis.min = this.normalizeAxisLimit(axis.min); + axis.max = this.normalizeAxisLimit(axis.max); + const minCfg = axis.min; + const maxCfg = axis.max; - const minCfg = axis.min as unknown as ValueSourceConfig; - const maxCfg = axis.max as unknown as ValueSourceConfig; - - const minValid = - minCfg?.type !== ValueSourceType.latestKey || - latestKeys.some(k => this.isYAxisKey(k, minCfg)); + const minValid = !!minCfg && ( + minCfg.type !== ValueSourceType.latestKey || + latestKeys.some(k => this.isYAxisKey(k, minCfg)) + ); - const maxValid = - maxCfg?.type !== ValueSourceType.latestKey || - latestKeys.some(k => this.isYAxisKey(k, maxCfg)); + const maxValid = !!maxCfg && ( + maxCfg.type !== ValueSourceType.latestKey || + latestKeys.some(k => this.isYAxisKey(k, maxCfg)) + ); if (minValid && maxValid) { result[id] = axis; @@ -288,17 +291,17 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, d.type === limit.latestKeyType); } - private normalizeAxisLimit(limit: any): ValueSourceConfig { + private normalizeAxisLimit(limit: string | number | ValueSourceConfig): ValueSourceConfig { if (!limit) { return { type: ValueSourceType.constant, value: null, entityAlias: null }; - } else if (typeof limit === 'number') { + } else if (typeof limit === 'number' || typeof limit === 'string') { return { type: ValueSourceType.constant, - value: limit, + value: Number(limit), entityAlias: null }; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html index 420e834106..6389bbd6f8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html @@ -30,14 +30,16 @@ -
+
- +
-
+
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts index 1857852afe..6f4c618394 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts @@ -43,6 +43,7 @@ import { deepClone } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { TimeSeriesChartYAxesPanelComponent } from '@home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component'; +import { ValueSourceType } from '@shared/models/widget-settings.models'; @Component({ selector: 'tb-time-series-chart-y-axis-row', @@ -99,8 +100,8 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O position: [null, []], units: [null, []], decimals: [null, []], - min: [null, []], - max: [null, []], + min: this.createLimitFormGroup(), + max: this.createLimitFormGroup(), show: [null, []] }); this.axisFormGroup.valueChanges.pipe( @@ -195,6 +196,10 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O } } + checkIsConstantLimit(limit: 'min' | 'max') { + return this.axisFormGroup.get(`${limit}.type`)?.value === ValueSourceType.constant; + } + private updateValidators() { const show: boolean = this.axisFormGroup.get('show').value; if (show) { @@ -208,6 +213,13 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O this.axisFormGroup.get('units').disable({emitEvent: false}); this.axisFormGroup.get('decimals').disable({emitEvent: false}); } + if(!this.checkIsConstantLimit('min')){ + this.axisFormGroup.get('min').disable({emitEvent: false}); + } + if(!this.checkIsConstantLimit('max')){ + this.axisFormGroup.get('max').disable({emitEvent: false}); + } + } private updateModel() { @@ -221,4 +233,12 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O this.modelValue.show = value.show; this.propagateChange(this.modelValue); } + + private createLimitFormGroup() { + return this.fb.group({ + type: [ValueSourceType.constant, []], + value: [null, []], + entityAlias: [null, []] + }) + } }