thingsboard/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts
2025-08-05 12:14:20 +03:00

272 lines
8.8 KiB
TypeScript

///
/// 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.
///
import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models';
import { DatasourceCallbacks } from '@home/components/widget/config/datasource.component.models';
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
import { Observable } from 'rxjs';
import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, Inject, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
import {
DataKey,
DatasourceType,
Widget,
widgetTypeCanHaveTimewindow,
WidgetConfigMode,
widgetType
} from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils';
import { IAliasController } from '@core/api/widget-api.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { initModelFromDefaultTimewindow } from '@shared/models/time/time.models';
export type WidgetConfigCallbacks = DatasourceCallbacks & WidgetActionCallbacks;
export interface IBasicWidgetConfigComponent {
isAdd: boolean;
widgetConfig: WidgetConfigComponentData;
widgetConfigChanged: Observable<WidgetConfigComponentData>;
validateConfig(): boolean;
}
@Directive()
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export abstract class BasicWidgetConfigComponent extends PageComponent implements
IBasicWidgetConfigComponent, OnInit, AfterViewInit {
isAdd = false;
basicMode = WidgetConfigMode.basic;
widgetConfigValue: WidgetConfigComponentData;
set widgetConfig(value: WidgetConfigComponentData) {
this.widgetConfigValue = value;
this.setupConfig(this.widgetConfigValue);
}
get widgetConfig(): WidgetConfigComponentData {
return this.widgetConfigValue;
}
get aliasController(): IAliasController {
return this.widgetConfigComponent.aliasController;
}
get callbacks(): WidgetConfigCallbacks {
return this.widgetConfigComponent.widgetConfigCallbacks;
}
get functionsOnly(): boolean {
return this.widgetConfigComponent.functionsOnly;
}
get widgetType(): widgetType {
return this.widgetConfigComponent.widgetType;
}
get widgetEditMode(): boolean {
return this.widgetConfigComponent.widgetEditMode;
}
get widget(): Widget {
return this.widgetConfigComponent.widget;
}
widgetConfigChangedEmitter = new EventEmitter<WidgetConfigComponentData>();
widgetConfigChanged = this.widgetConfigChangedEmitter.asObservable();
protected destroyRef = inject(DestroyRef);
protected constructor(@Inject(Store) protected store: Store<AppState>,
protected widgetConfigComponent: WidgetConfigComponent) {
super(store);
}
ngOnInit() {}
ngAfterViewInit(): void {
if (!this.validateConfig()) {
setTimeout(() => {
this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue()));
}, 0);
}
}
protected setupConfig(widgetConfig: WidgetConfigComponentData) {
if (this.isAdd) {
this.setupDefaults(widgetConfig);
}
if (widgetTypeCanHaveTimewindow(widgetConfig.widgetType) && isUndefinedOrNull(widgetConfig.config.timewindow)) {
widgetConfig.config.timewindow = initModelFromDefaultTimewindow(null,
widgetConfig.widgetType === widgetType.latest, false, this.widgetConfigComponent.timeService,
widgetConfig.widgetType === widgetType.timeseries);
}
this.onConfigSet(widgetConfig);
this.updateValidators(false);
for (const trigger of this.validatorTriggers()) {
const path = trigger.split('.');
let control: AbstractControl = this.configForm();
for (const part of path) {
control = control.get(part);
}
control.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(true, trigger);
});
}
this.configForm().valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue()));
});
}
protected setupDefaults(configData: WidgetConfigComponentData) {
const params = configData.typeParameters;
let dataKeys: DataKey[];
let latestDataKeys: DataKey[];
if (params.defaultDataKeysFunction) {
dataKeys = params.defaultDataKeysFunction(this, configData);
}
if (params.defaultLatestDataKeysFunction) {
latestDataKeys = params.defaultLatestDataKeysFunction(this, configData);
}
if (!dataKeys) {
dataKeys = this.defaultDataKeys(configData);
}
if (!latestDataKeys) {
latestDataKeys = this.defaultLatestDataKeys(configData);
}
if (dataKeys || latestDataKeys) {
this.setupDefaultDatasource(configData, dataKeys, latestDataKeys);
}
}
protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] {
return null;
}
protected defaultLatestDataKeys(configData: WidgetConfigComponentData): DataKey[] {
return null;
}
protected updateValidators(emitEvent: boolean, trigger?: string) {
}
protected validatorTriggers(): string[] {
return [];
}
protected onConfigChanged(widgetConfig: WidgetConfigComponentData) {
this.widgetConfigValue = widgetConfig;
this.widgetConfigChangedEmitter.emit(this.widgetConfigValue);
}
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
return config;
}
public validateConfig(): boolean {
return this.configForm().valid;
}
protected setupDefaultDatasource(configData: WidgetConfigComponentData, keys?: DataKey[], latestKeys?: DataKey[]) {
let datasources = configData.config.datasources;
if (!datasources || !datasources.length) {
datasources = [
{
type: DatasourceType.device,
dataKeys: []
}
];
configData.config.datasources = datasources;
}
let dataKeys = datasources[0].dataKeys;
if (!dataKeys) {
dataKeys = [];
datasources[0].dataKeys = dataKeys;
}
let latestDataKeys = datasources[0].latestDataKeys;
if (!latestDataKeys) {
latestDataKeys = [];
datasources[0].latestDataKeys = latestDataKeys;
}
if (keys && keys.length) {
dataKeys.length = 0;
keys.forEach(key => {
const dataKey = this.constructDataKey(configData, key, false);
dataKeys.push(dataKey);
});
}
if (latestKeys && latestKeys.length) {
latestDataKeys.length = 0;
latestKeys.forEach(key => {
const dataKey = this.constructDataKey(configData, key, true);
latestDataKeys.push(dataKey);
});
}
}
protected constructDataKey(configData: WidgetConfigComponentData, key: DataKey, isLatestKey: boolean): DataKey {
const dataKey =
this.widgetConfigComponent.widgetConfigCallbacks.generateDataKey(key.name, key.type,
configData.dataKeySettingsForm, isLatestKey, configData.dataKeySettingsFunction);
if (key.label) {
dataKey.label = key.label;
}
if (key.units) {
dataKey.units = key.units;
}
if (isDefinedAndNotNull(key.decimals)) {
dataKey.decimals = key.decimals;
}
if (key.color) {
dataKey.color = key.color;
}
if (isDefinedAndNotNull(key.settings)) {
dataKey.settings = key.settings;
}
if (isDefinedAndNotNull(key.aggregationType)) {
dataKey.aggregationType = key.aggregationType;
}
if (isDefinedAndNotNull(key.comparisonEnabled)) {
dataKey.comparisonEnabled = key.comparisonEnabled;
}
if (isDefinedAndNotNull(key.timeForComparison)) {
dataKey.timeForComparison = key.timeForComparison;
}
if (isDefinedAndNotNull(key.comparisonCustomIntervalValue)) {
dataKey.comparisonCustomIntervalValue = key.comparisonCustomIntervalValue;
}
if (isDefinedAndNotNull(key.comparisonResultType)) {
dataKey.comparisonResultType = key.comparisonResultType;
}
return dataKey;
}
protected abstract configForm(): UntypedFormGroup;
protected abstract onConfigSet(configData: WidgetConfigComponentData);
}