Ability to define component form for advanced widget settings and widget data keys
This commit is contained in:
parent
d7efbd6eba
commit
c2f4724ca2
@ -91,7 +91,9 @@ export class AddWidgetDialogComponent extends DialogComponent<AddWidgetDialogCom
|
||||
actionSources,
|
||||
isDataEnabled,
|
||||
settingsSchema,
|
||||
dataKeySettingsSchema
|
||||
dataKeySettingsSchema,
|
||||
settingsDirective: widgetInfo.settingsDirective,
|
||||
dataKeySettingsDirective: widgetInfo.dataKeySettingsDirective
|
||||
};
|
||||
|
||||
this.widgetFormGroup = this.fb.group({
|
||||
|
||||
@ -112,7 +112,9 @@ export class EditWidgetComponent extends PageComponent implements OnInit, OnChan
|
||||
actionSources,
|
||||
isDataEnabled,
|
||||
settingsSchema,
|
||||
dataKeySettingsSchema
|
||||
dataKeySettingsSchema,
|
||||
settingsDirective: widgetInfo.settingsDirective,
|
||||
dataKeySettingsDirective: widgetInfo.dataKeySettingsDirective
|
||||
};
|
||||
this.widgetFormGroup.reset({widgetConfig: this.widgetConfig});
|
||||
}
|
||||
|
||||
@ -148,6 +148,8 @@ import {
|
||||
} from '@home/components/tokens';
|
||||
import { DashboardStateComponent } from '@home/components/dashboard-page/dashboard-state.component';
|
||||
import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component';
|
||||
import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widget-settings.module';
|
||||
import { WidgetSettingsComponent } from '@home/components/widget/widget-settings.component';
|
||||
|
||||
@NgModule({
|
||||
declarations:
|
||||
@ -182,6 +184,7 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai
|
||||
WidgetContainerComponent,
|
||||
WidgetComponent,
|
||||
LegendComponent,
|
||||
WidgetSettingsComponent,
|
||||
WidgetConfigComponent,
|
||||
EntityFilterViewComponent,
|
||||
EntityFilterComponent,
|
||||
@ -273,6 +276,7 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
SharedHomeComponentsModule,
|
||||
WidgetSettingsModule,
|
||||
Lwm2mProfileComponentsModule,
|
||||
SnmpDeviceProfileTransportModule,
|
||||
StatesControllerModule,
|
||||
@ -301,6 +305,7 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai
|
||||
WidgetContainerComponent,
|
||||
WidgetComponent,
|
||||
LegendComponent,
|
||||
WidgetSettingsComponent,
|
||||
WidgetConfigComponent,
|
||||
EntityFilterViewComponent,
|
||||
EntityFilterComponent,
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
<div mat-dialog-content>
|
||||
<tb-data-key-config #dataKeyConfig
|
||||
[dataKeySettingsSchema]="data.dataKeySettingsSchema"
|
||||
[dataKeySettingsDirective]="data.dataKeySettingsDirective"
|
||||
[entityAliasId]="data.entityAliasId"
|
||||
[showPostProcessing]="data.showPostProcessing"
|
||||
[callbacks]="data.callbacks"
|
||||
|
||||
@ -29,6 +29,7 @@ import { DataKeyConfigComponent } from '@home/components/widget/data-key-config.
|
||||
export interface DataKeyConfigDialogData {
|
||||
dataKey: DataKey;
|
||||
dataKeySettingsSchema: any;
|
||||
dataKeySettingsDirective: string;
|
||||
entityAliasId?: string;
|
||||
showPostProcessing?: boolean;
|
||||
callbacks?: DataKeysCallbacks;
|
||||
|
||||
@ -98,9 +98,10 @@
|
||||
</mat-tab>
|
||||
<mat-tab [formGroup]="dataKeySettingsFormGroup" label="{{ 'datakey.advanced' | translate }}" *ngIf="displayAdvanced">
|
||||
<div class="mat-padding" fxLayout="column">
|
||||
<tb-json-form
|
||||
<tb-widget-settings
|
||||
[settingsDirective]="dataKeySettingsDirective"
|
||||
formControlName="settings">
|
||||
</tb-json-form>
|
||||
</tb-widget-settings>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
|
||||
@ -72,6 +72,9 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
|
||||
@Input()
|
||||
dataKeySettingsSchema: any;
|
||||
|
||||
@Input()
|
||||
dataKeySettingsDirective: string;
|
||||
|
||||
@Input()
|
||||
showPostProcessing = true;
|
||||
|
||||
@ -121,11 +124,15 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
|
||||
type: DataKeyType.alarm
|
||||
});
|
||||
}
|
||||
if (this.dataKeySettingsSchema && this.dataKeySettingsSchema.schema) {
|
||||
if (this.dataKeySettingsSchema && this.dataKeySettingsSchema.schema ||
|
||||
this.dataKeySettingsDirective && this.dataKeySettingsDirective.length) {
|
||||
this.displayAdvanced = true;
|
||||
this.dataKeySettingsData = {
|
||||
schema: this.dataKeySettingsSchema.schema,
|
||||
form: this.dataKeySettingsSchema.form || ['*']
|
||||
schema: this.dataKeySettingsSchema?.schema || {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
},
|
||||
form: this.dataKeySettingsSchema?.form || ['*']
|
||||
};
|
||||
this.dataKeySettingsFormGroup = this.fb.group({
|
||||
settings: [null, []]
|
||||
|
||||
@ -113,6 +113,9 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie
|
||||
@Input()
|
||||
datakeySettingsSchema: any;
|
||||
|
||||
@Input()
|
||||
dataKeySettingsDirective: string;
|
||||
|
||||
@Input()
|
||||
callbacks: DataKeysCallbacks;
|
||||
|
||||
@ -395,6 +398,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie
|
||||
data: {
|
||||
dataKey: deepClone(key),
|
||||
dataKeySettingsSchema: this.datakeySettingsSchema,
|
||||
dataKeySettingsDirective: this.dataKeySettingsDirective,
|
||||
entityAliasId: this.entityAliasId,
|
||||
showPostProcessing: this.widgetType !== widgetType.alarm,
|
||||
callbacks: this.callbacks
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2022 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="qrCodeWidgetSettingsForm" fxLayout="column">
|
||||
<mat-checkbox formControlName="useQrCodeTextFunction" style="padding-bottom: 16px;">
|
||||
{{ 'widgets.qr-code.use-qr-code-text-function' | translate }}
|
||||
</mat-checkbox>
|
||||
<mat-form-field [fxShow]="qrCodeWidgetSettingsForm.get('useQrCodeTextFunction').value === false">
|
||||
<mat-label translate>widgets.qr-code.qr-code-text-pattern</mat-label>
|
||||
<input required matInput formControlName="qrCodeTextPattern">
|
||||
<mat-error *ngIf="qrCodeWidgetSettingsForm.get('qrCodeTextPattern').hasError('required')">
|
||||
{{ 'widgets.qr-code.qr-code-text-pattern-required' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<section fxLayout="column" [fxShow]="qrCodeWidgetSettingsForm.get('useQrCodeTextFunction').value === true">
|
||||
<label translate class="tb-title no-padding">widgets.qr-code.qr-code-text-function</label>
|
||||
<tb-js-func formControlName="qrCodeTextFunction"
|
||||
[functionArgs]="['data']"
|
||||
helpId="widget/lib/qrcode/qrcode_text_fn">
|
||||
</tb-js-func>
|
||||
</section>
|
||||
</section>
|
||||
@ -0,0 +1,67 @@
|
||||
///
|
||||
/// Copyright © 2016-2022 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 { Component } from '@angular/core';
|
||||
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'tb-qrcode-widget-settings',
|
||||
templateUrl: './qrcode-widget-settings.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class QrCodeWidgetSettingsComponent extends WidgetSettingsComponent {
|
||||
|
||||
qrCodeWidgetSettingsForm: FormGroup;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
private fb: FormBuilder) {
|
||||
super(store);
|
||||
}
|
||||
|
||||
protected settingsForm(): FormGroup {
|
||||
return this.qrCodeWidgetSettingsForm;
|
||||
}
|
||||
|
||||
protected onSettingsSet(settings: WidgetSettings) {
|
||||
this.qrCodeWidgetSettingsForm = this.fb.group({
|
||||
qrCodeTextPattern: [settings ? settings.qrCodeTextPattern : '${entityName}', []],
|
||||
useQrCodeTextFunction: [settings ? settings.useQrCodeTextFunction : false, []],
|
||||
qrCodeTextFunction: [settings ? settings.qrCodeTextFunction : 'return data[0][\'entityName\'];', []]
|
||||
});
|
||||
}
|
||||
|
||||
protected validatorTriggers(): string[] {
|
||||
return ['useQrCodeTextFunction'];
|
||||
}
|
||||
|
||||
protected updateValidators(emitEvent: boolean) {
|
||||
const useQrCodeTextFunction: boolean = this.qrCodeWidgetSettingsForm.get('useQrCodeTextFunction').value;
|
||||
if (useQrCodeTextFunction) {
|
||||
this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').setValidators([]);
|
||||
this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').setValidators([Validators.required]);
|
||||
} else {
|
||||
this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').setValidators([Validators.required]);
|
||||
this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').setValidators([]);
|
||||
}
|
||||
this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').updateValueAndValidity({emitEvent});
|
||||
this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').updateValueAndValidity({emitEvent});
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
///
|
||||
/// Copyright © 2016-2022 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 { NgModule, Type } from '@angular/core';
|
||||
import { QrCodeWidgetSettingsComponent } from '@home/components/widget/lib/settings/qrcode-widget-settings.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module';
|
||||
import { IWidgetSettingsComponent } from '@shared/models/widget.models';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
QrCodeWidgetSettingsComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
SharedHomeComponentsModule
|
||||
],
|
||||
exports: [
|
||||
QrCodeWidgetSettingsComponent
|
||||
]
|
||||
})
|
||||
export class WidgetSettingsModule {
|
||||
}
|
||||
|
||||
export const widgetSettingsComponentsMap: {[key: string]: Type<IWidgetSettingsComponent>} = {
|
||||
'tb-qrcode-widget-settings': QrCodeWidgetSettingsComponent
|
||||
};
|
||||
@ -107,6 +107,8 @@ export class WidgetComponentService {
|
||||
controllerScript: this.utils.editWidgetInfo.controllerScript,
|
||||
settingsSchema: this.utils.editWidgetInfo.settingsSchema,
|
||||
dataKeySettingsSchema: this.utils.editWidgetInfo.dataKeySettingsSchema,
|
||||
settingsDirective: this.utils.editWidgetInfo.settingsDirective,
|
||||
dataKeySettingsDirective: this.utils.editWidgetInfo.dataKeySettingsDirective,
|
||||
defaultConfig: this.utils.editWidgetInfo.defaultConfig
|
||||
}, new WidgetTypeId('1'), new TenantId( NULL_UUID ), 'customWidgetBundle', undefined
|
||||
);
|
||||
|
||||
@ -190,6 +190,7 @@
|
||||
[optDataKeys]="modelValue?.typeParameters?.dataKeysOptional"
|
||||
[aliasController]="aliasController"
|
||||
[datakeySettingsSchema]="modelValue?.dataKeySettingsSchema"
|
||||
[dataKeySettingsDirective]="modelValue?.dataKeySettingsDirective"
|
||||
[callbacks]="widgetConfigCallbacks"
|
||||
[entityAliasId]="datasourceControl.get('entityAliasId').value"
|
||||
[formControl]="datasourceControl.get('dataKeys')">
|
||||
@ -283,6 +284,7 @@
|
||||
[datasourceType]="alarmSourceSettings.get('type').value"
|
||||
[aliasController]="aliasController"
|
||||
[datakeySettingsSchema]="modelValue?.dataKeySettingsSchema"
|
||||
[dataKeySettingsDirective]="modelValue?.dataKeySettingsDirective"
|
||||
[callbacks]="widgetConfigCallbacks"
|
||||
[entityAliasId]="alarmSourceSettings.get('entityAliasId').value"
|
||||
formControlName="dataKeys">
|
||||
@ -476,9 +478,10 @@
|
||||
<mat-tab *ngIf="displayAdvanced()" label="{{ 'widget-config.advanced' | translate }}">
|
||||
<div [formGroup]="advancedSettings" class="mat-content mat-padding tb-advanced-widget-config"
|
||||
fxLayout="column">
|
||||
<tb-json-form
|
||||
<tb-widget-settings
|
||||
[settingsDirective]="modelValue.settingsDirective"
|
||||
formControlName="settings">
|
||||
</tb-json-form>
|
||||
</tb-widget-settings>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="{{ 'widget-config.actions' | translate }}" [formGroup]="actionsSettings">
|
||||
|
||||
@ -605,7 +605,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
|
||||
widgetSettingsFormData.schema = deepClone(emptySettingsSchema);
|
||||
widgetSettingsFormData.form = deepClone(defaultSettingsForm);
|
||||
widgetSettingsFormData.groupInfoes = deepClone(emptySettingsGroupInfoes);
|
||||
widgetSettingsFormData.model = {};
|
||||
widgetSettingsFormData.model = settings || {};
|
||||
}
|
||||
this.advancedSettings.patchValue({ settings: widgetSettingsFormData }, {emitEvent: false});
|
||||
}
|
||||
@ -713,7 +713,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
|
||||
}
|
||||
|
||||
public displayAdvanced(): boolean {
|
||||
return !!this.modelValue && !!this.modelValue.settingsSchema && !!this.modelValue.settingsSchema.schema;
|
||||
return !!this.modelValue && (!!this.modelValue.settingsSchema && !!this.modelValue.settingsSchema.schema ||
|
||||
!!this.modelValue.settingsDirective && !!this.modelValue.settingsDirective.length);
|
||||
}
|
||||
|
||||
public dndDatasourceMoved(index: number) {
|
||||
@ -913,7 +914,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
|
||||
valid: false
|
||||
}
|
||||
};
|
||||
} else if (!this.advancedSettings.valid) {
|
||||
} else if (!this.advancedSettings.valid || (this.displayAdvanced() && !this.modelValue.config.settings)) {
|
||||
return {
|
||||
advancedSettings: {
|
||||
valid: false
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2022 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.
|
||||
|
||||
-->
|
||||
<div [formGroup]="widgetSettingsFormGroup">
|
||||
<ng-container #definedSettingsContent></ng-container>
|
||||
<div class="tb-settings-directive-error" *ngIf="definedDirectiveError">{{definedDirectiveError}}</div>
|
||||
<tb-json-form *ngIf="useJsonForm()" #jsonFormComponent
|
||||
formControlName="settings">
|
||||
</tb-json-form>
|
||||
</div>
|
||||
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright © 2016-2022 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.
|
||||
*/
|
||||
:host {
|
||||
.tb-settings-directive-error {
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: rgb(221, 44, 0);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,201 @@
|
||||
///
|
||||
/// Copyright © 2016-2022 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 {
|
||||
AfterViewInit,
|
||||
Component, ComponentFactoryResolver,
|
||||
ComponentRef,
|
||||
forwardRef,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ViewContainerRef
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
|
||||
import {
|
||||
IRuleNodeConfigurationComponent,
|
||||
RuleNodeConfiguration,
|
||||
RuleNodeDefinition
|
||||
} from '@shared/models/rule-node.models';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { RuleChainService } from '@core/http/rule-chain.service';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { JsonObjectEditComponent } from '@shared/components/json-object-edit.component';
|
||||
import { deepClone } from '@core/utils';
|
||||
import { RuleChainType } from '@shared/models/rule-chain.models';
|
||||
import { JsonFormComponent } from '@shared/components/json-form/json-form.component';
|
||||
import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models';
|
||||
import { IWidgetSettingsComponent, WidgetSettings } from '@shared/models/widget.models';
|
||||
import { widgetSettingsComponentsMap } from '@home/components/widget/lib/settings/widget-settings.module';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-widget-settings',
|
||||
templateUrl: './widget-settings.component.html',
|
||||
styleUrls: ['./widget-settings.component.scss'],
|
||||
providers: [{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => WidgetSettingsComponent),
|
||||
multi: true
|
||||
}]
|
||||
})
|
||||
export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
@ViewChild('definedSettingsContent', {read: ViewContainerRef, static: true}) definedSettingsContainer: ViewContainerRef;
|
||||
|
||||
@ViewChild('jsonFormComponent') jsonFormComponent: JsonFormComponent;
|
||||
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
settingsDirectiveValue: string;
|
||||
|
||||
@Input()
|
||||
set settingsDirective(settingsDirective: string) {
|
||||
if (this.settingsDirectiveValue !== settingsDirective) {
|
||||
this.settingsDirectiveValue = settingsDirective;
|
||||
if (this.settingsDirectiveValue) {
|
||||
this.validateDefinedDirective();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get settingsDirective(): string {
|
||||
return this.settingsDirectiveValue;
|
||||
}
|
||||
|
||||
definedDirectiveError: string;
|
||||
|
||||
widgetSettingsFormGroup: FormGroup;
|
||||
|
||||
changeSubscription: Subscription;
|
||||
|
||||
private definedSettingsComponentRef: ComponentRef<IWidgetSettingsComponent>;
|
||||
private definedSettingsComponent: IWidgetSettingsComponent;
|
||||
|
||||
private widgetSettingsFormData: JsonFormComponentData;
|
||||
|
||||
private propagateChange = (v: any) => { };
|
||||
|
||||
constructor(private translate: TranslateService,
|
||||
private cfr: ComponentFactoryResolver,
|
||||
private fb: FormBuilder) {
|
||||
this.widgetSettingsFormGroup = this.fb.group({
|
||||
settings: [null, Validators.required]
|
||||
});
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.propagateChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.definedSettingsComponentRef) {
|
||||
this.definedSettingsComponentRef.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
if (this.disabled) {
|
||||
this.widgetSettingsFormGroup.disable({emitEvent: false});
|
||||
} else {
|
||||
this.widgetSettingsFormGroup.enable({emitEvent: false});
|
||||
}
|
||||
}
|
||||
|
||||
writeValue(value: JsonFormComponentData): void {
|
||||
this.widgetSettingsFormData = value;
|
||||
if (this.changeSubscription) {
|
||||
this.changeSubscription.unsubscribe();
|
||||
this.changeSubscription = null;
|
||||
}
|
||||
if (this.definedSettingsComponent) {
|
||||
this.definedSettingsComponent.settings = this.widgetSettingsFormData.model;
|
||||
this.changeSubscription = this.definedSettingsComponent.settingsChanged.subscribe((settings) => {
|
||||
this.updateModel(settings);
|
||||
});
|
||||
} else {
|
||||
this.widgetSettingsFormGroup.get('settings').patchValue(this.widgetSettingsFormData, {emitEvent: false});
|
||||
this.changeSubscription = this.widgetSettingsFormGroup.get('settings').valueChanges.subscribe(
|
||||
(widgetSettingsFormData: JsonFormComponentData) => {
|
||||
this.updateModel(widgetSettingsFormData.model);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
useDefinedDirective(): boolean {
|
||||
return this.settingsDirective &&
|
||||
this.settingsDirective.length && !this.definedDirectiveError;
|
||||
}
|
||||
|
||||
useJsonForm(): boolean {
|
||||
return !this.settingsDirective || !this.settingsDirective.length;
|
||||
}
|
||||
|
||||
private updateModel(settings: WidgetSettings) {
|
||||
this.widgetSettingsFormData.model = settings;
|
||||
if (this.definedSettingsComponent || this.widgetSettingsFormGroup.valid) {
|
||||
this.propagateChange(this.widgetSettingsFormData);
|
||||
} else {
|
||||
this.propagateChange(null);
|
||||
}
|
||||
}
|
||||
|
||||
private validateDefinedDirective() {
|
||||
if (this.definedSettingsComponentRef) {
|
||||
this.definedSettingsComponentRef.destroy();
|
||||
this.definedSettingsComponentRef = null;
|
||||
}
|
||||
if (this.settingsDirective && this.settingsDirective.length) {
|
||||
const componentType = widgetSettingsComponentsMap[this.settingsDirective];
|
||||
if (!componentType) {
|
||||
this.definedDirectiveError = this.translate.instant('widget-config.settings-component-not-found',
|
||||
{selector: this.settingsDirective});
|
||||
} else {
|
||||
if (this.changeSubscription) {
|
||||
this.changeSubscription.unsubscribe();
|
||||
this.changeSubscription = null;
|
||||
}
|
||||
this.definedSettingsContainer.clear();
|
||||
const factory = this.cfr.resolveComponentFactory(componentType);
|
||||
this.definedSettingsComponentRef = this.definedSettingsContainer.createComponent(factory);
|
||||
this.definedSettingsComponent = this.definedSettingsComponentRef.instance;
|
||||
this.definedSettingsComponent.settings = this.widgetSettingsFormData?.model;
|
||||
this.changeSubscription = this.definedSettingsComponent.settingsChanged.subscribe((settings) => {
|
||||
this.updateModel(settings);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validate() {
|
||||
if (this.useDefinedDirective()) {
|
||||
this.definedSettingsComponent.validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -424,6 +424,8 @@ export interface WidgetConfigComponentData {
|
||||
isDataEnabled: boolean;
|
||||
settingsSchema: JsonSettingsSchema;
|
||||
dataKeySettingsSchema: JsonSettingsSchema;
|
||||
settingsDirective: string;
|
||||
dataKeySettingsDirective: string;
|
||||
}
|
||||
|
||||
export const MissingWidgetType: WidgetInfo = {
|
||||
@ -510,6 +512,8 @@ export function toWidgetInfo(widgetTypeEntity: WidgetType): WidgetInfo {
|
||||
controllerScript: widgetTypeEntity.descriptor.controllerScript,
|
||||
settingsSchema: widgetTypeEntity.descriptor.settingsSchema,
|
||||
dataKeySettingsSchema: widgetTypeEntity.descriptor.dataKeySettingsSchema,
|
||||
settingsDirective: widgetTypeEntity.descriptor.settingsDirective,
|
||||
dataKeySettingsDirective: widgetTypeEntity.descriptor.dataKeySettingsDirective,
|
||||
defaultConfig: widgetTypeEntity.descriptor.defaultConfig
|
||||
};
|
||||
}
|
||||
@ -536,6 +540,8 @@ export function toWidgetType(widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId:
|
||||
controllerScript: widgetInfo.controllerScript,
|
||||
settingsSchema: widgetInfo.settingsSchema,
|
||||
dataKeySettingsSchema: widgetInfo.dataKeySettingsSchema,
|
||||
settingsDirective: widgetInfo.settingsDirective,
|
||||
dataKeySettingsDirective: widgetInfo.dataKeySettingsDirective,
|
||||
defaultConfig: widgetInfo.defaultConfig
|
||||
};
|
||||
return {
|
||||
|
||||
@ -237,6 +237,18 @@
|
||||
rows="2" maxlength="255"></textarea>
|
||||
<mat-hint align="end">{{descriptionInput.value?.length || 0}}/255</mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>widget.settings-form-selector</mat-label>
|
||||
<input matInput
|
||||
[(ngModel)]="widget.settingsDirective"
|
||||
(ngModelChange)="isDirty = true"/>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>widget.data-key-settings-form-selector</mat-label>
|
||||
<input matInput
|
||||
[(ngModel)]="widget.dataKeySettingsDirective"
|
||||
(ngModelChange)="isDirty = true"/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
@ -25,6 +25,14 @@ import { EntityId } from '@shared/models/id/entity-id';
|
||||
import * as moment_ from 'moment';
|
||||
import { EntityDataPageLink, EntityFilter, KeyFilter } from '@shared/models/query/query.models';
|
||||
import { PopoverPlacement } from '@shared/components/popover.models';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
import { AfterViewInit, Directive, EventEmitter, Inject, OnInit } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { AbstractControl, FormGroup } from '@angular/forms';
|
||||
import {RuleChainType} from "@shared/models/rule-chain.models";
|
||||
import {Observable} from "rxjs";
|
||||
import {RuleNodeConfiguration} from "@shared/models/rule-node.models";
|
||||
|
||||
export enum widgetType {
|
||||
timeseries = 'timeseries',
|
||||
@ -143,6 +151,8 @@ export interface WidgetTypeDescriptor {
|
||||
controllerScript: string;
|
||||
settingsSchema?: string | any;
|
||||
dataKeySettingsSchema?: string | any;
|
||||
settingsDirective?: string;
|
||||
dataKeySettingsDirective?: string;
|
||||
defaultConfig: string;
|
||||
sizeX: number;
|
||||
sizeY: number;
|
||||
@ -159,6 +169,7 @@ export interface WidgetTypeParameters {
|
||||
singleEntity?: boolean;
|
||||
warnOnPageDataOverflow?: boolean;
|
||||
ignoreDataUpdateOnIntervalTick?: boolean;
|
||||
|
||||
}
|
||||
|
||||
export interface WidgetControllerDescriptor {
|
||||
@ -483,6 +494,10 @@ export interface WidgetComparisonSettings {
|
||||
comparisonCustomIntervalValue?: number;
|
||||
}
|
||||
|
||||
export interface WidgetSettings {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface WidgetConfig {
|
||||
title?: string;
|
||||
titleIcon?: string;
|
||||
@ -512,7 +527,7 @@ export interface WidgetConfig {
|
||||
decimals?: number;
|
||||
noDataDisplayMessage?: string;
|
||||
actions?: {[actionSourceId: string]: Array<WidgetActionDescriptor>};
|
||||
settings?: any;
|
||||
settings?: WidgetSettings;
|
||||
alarmSource?: Datasource;
|
||||
alarmStatusList?: AlarmSearchStatus[];
|
||||
alarmSeverityList?: AlarmSeverity[];
|
||||
@ -570,3 +585,113 @@ export interface WidgetSize {
|
||||
sizeX: number;
|
||||
sizeY: number;
|
||||
}
|
||||
|
||||
export interface IWidgetSettingsComponent {
|
||||
settings: WidgetSettings;
|
||||
settingsChanged: Observable<WidgetSettings>;
|
||||
validate();
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Directive()
|
||||
// tslint:disable-next-line:directive-class-suffix
|
||||
export abstract class WidgetSettingsComponent extends PageComponent implements
|
||||
IWidgetSettingsComponent, OnInit, AfterViewInit {
|
||||
|
||||
settingsValue: WidgetSettings;
|
||||
|
||||
private settingsSet = false;
|
||||
|
||||
set settings(value: WidgetSettings) {
|
||||
this.settingsValue = value;
|
||||
if (!this.settingsSet) {
|
||||
this.settingsSet = true;
|
||||
this.setupSettings(value);
|
||||
} else {
|
||||
this.updateSettings(value);
|
||||
}
|
||||
}
|
||||
|
||||
get settings(): WidgetSettings {
|
||||
return this.settingsValue;
|
||||
}
|
||||
|
||||
settingsChangedEmitter = new EventEmitter<WidgetSettings>();
|
||||
settingsChanged = this.settingsChangedEmitter.asObservable();
|
||||
|
||||
protected constructor(@Inject(Store) protected store: Store<AppState>) {
|
||||
super(store);
|
||||
}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
setTimeout(() => {
|
||||
if (!this.validateSettings()) {
|
||||
this.settingsChangedEmitter.emit(null);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
validate() {
|
||||
this.onValidate();
|
||||
}
|
||||
|
||||
protected setupSettings(settings: WidgetSettings) {
|
||||
this.onSettingsSet(this.prepareInputSettings(settings));
|
||||
this.updateValidators(false);
|
||||
for (const trigger of this.validatorTriggers()) {
|
||||
const path = trigger.split('.');
|
||||
let control: AbstractControl = this.settingsForm();
|
||||
for (const part of path) {
|
||||
control = control.get(part);
|
||||
}
|
||||
control.valueChanges.subscribe(() => {
|
||||
this.updateValidators(true, trigger);
|
||||
});
|
||||
}
|
||||
this.settingsForm().valueChanges.subscribe((updated: WidgetSettings) => {
|
||||
this.onSettingsChanged(updated);
|
||||
});
|
||||
}
|
||||
|
||||
protected updateSettings(settings: WidgetSettings) {
|
||||
this.settingsForm().reset(this.prepareInputSettings(settings), {emitEvent: false});
|
||||
this.updateValidators(false);
|
||||
}
|
||||
|
||||
protected updateValidators(emitEvent: boolean, trigger?: string) {
|
||||
}
|
||||
|
||||
protected validatorTriggers(): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected onSettingsChanged(updated: WidgetSettings) {
|
||||
this.settingsValue = updated;
|
||||
if (this.validateSettings()) {
|
||||
this.settingsChangedEmitter.emit(this.prepareOutputSettings(updated));
|
||||
} else {
|
||||
this.settingsChangedEmitter.emit(null);
|
||||
}
|
||||
}
|
||||
|
||||
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings {
|
||||
return settings;
|
||||
}
|
||||
|
||||
protected prepareOutputSettings(settings: WidgetSettings): WidgetSettings {
|
||||
return settings;
|
||||
}
|
||||
|
||||
protected validateSettings(): boolean {
|
||||
return this.settingsForm().valid;
|
||||
}
|
||||
|
||||
protected onValidate() {}
|
||||
|
||||
protected abstract settingsForm(): FormGroup;
|
||||
|
||||
protected abstract onSettingsSet(settings: WidgetSettings);
|
||||
|
||||
}
|
||||
|
||||
@ -2985,6 +2985,8 @@
|
||||
"widget-settings": "Widget settings",
|
||||
"description": "Description",
|
||||
"image-preview": "Image preview",
|
||||
"settings-form-selector": "Settings form selector",
|
||||
"data-key-settings-form-selector": "Data key settings form selector",
|
||||
"javascript": "Javascript",
|
||||
"js": "JS",
|
||||
"remove-widget-type-title": "Are you sure you want to remove the widget type '{{widgetName}}'?",
|
||||
@ -3151,7 +3153,8 @@
|
||||
"icon-size": "Icon size",
|
||||
"advanced-settings": "Advanced settings",
|
||||
"data-settings": "Data settings",
|
||||
"no-data-display-message": "\"No data to display\" alternative message"
|
||||
"no-data-display-message": "\"No data to display\" alternative message",
|
||||
"settings-component-not-found": "Settings form component not found for selector '{{selector}}'"
|
||||
},
|
||||
"widget-type": {
|
||||
"import": "Import widget type",
|
||||
@ -3266,6 +3269,12 @@
|
||||
"value": "Value"
|
||||
},
|
||||
"invalid-qr-code-text": "Invalid input text for QR code. Input should have a string type",
|
||||
"qr-code": {
|
||||
"use-qr-code-text-function": "Use QR code text function",
|
||||
"qr-code-text-pattern": "QR code text pattern (for ex. '${entityName} | ${keyName} - some text.')",
|
||||
"qr-code-text-pattern-required": "QR code text pattern is required.",
|
||||
"qr-code-text-function": "QR code text function"
|
||||
},
|
||||
"persistent-table": {
|
||||
"rpc-id": "RPC ID",
|
||||
"message-type": "Message type",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user