UI: Improvement multiple attribute widget
This commit is contained in:
parent
783552f6c3
commit
49a9a3136f
File diff suppressed because one or more lines are too long
@ -114,6 +114,10 @@ export function isNumeric(value: any): boolean {
|
||||
return (value - parseFloat(value) + 1) >= 0;
|
||||
}
|
||||
|
||||
export function isBoolean(value: any): boolean {
|
||||
return typeof value === 'boolean';
|
||||
}
|
||||
|
||||
export function isString(value: any): boolean {
|
||||
return typeof value === 'string';
|
||||
}
|
||||
|
||||
@ -37,7 +37,12 @@
|
||||
type="text"
|
||||
(focus)="key.isFocused = true; focusInputElement($event)"
|
||||
(blur)="key.isFocused = false; inputChanged(source, key)">
|
||||
<mat-icon *ngIf="key.settings.icon" matPrefix>{{key.settings.icon}}</mat-icon>
|
||||
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon" matPrefix>
|
||||
<mat-icon *ngIf="!key.settings.safeCustomIcon; else customToggleIcon">{{key.settings.icon}}</mat-icon>
|
||||
<ng-template #customToggleIcon>
|
||||
<img class="mat-icon" [src]="key.settings.safeCustomIcon" alt="icon">
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
|
||||
{{ getErrorMessageText(key.settings, 'required') }}
|
||||
</mat-error>
|
||||
@ -57,7 +62,12 @@
|
||||
max="{{key.settings.maxValue}}"
|
||||
(focus)="key.isFocused = true; focusInputElement($event)"
|
||||
(blur)="key.isFocused = false; inputChanged(source, key)">
|
||||
<mat-icon *ngIf="key.settings.icon" matPrefix>{{key.settings.icon}}</mat-icon>
|
||||
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon" matPrefix>
|
||||
<mat-icon *ngIf="!key.settings.safeCustomIcon; else customToggleIcon">{{key.settings.icon}}</mat-icon>
|
||||
<ng-template #customToggleIcon>
|
||||
<img class="mat-icon" [src]="key.settings.safeCustomIcon" alt="icon">
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
|
||||
{{ getErrorMessageText(key.settings,'required') }}
|
||||
</mat-error>
|
||||
@ -77,7 +87,14 @@
|
||||
</div>
|
||||
<div class="input-field mat-block" *ngIf="key.settings.dataKeyValueType === 'booleanSwitch'">
|
||||
<mat-slide-toggle formControlName="{{key.formId}}"
|
||||
[labelPosition]="key.settings.slideToggleLabelPosition"
|
||||
(change)="inputChanged(source, key)">
|
||||
<ng-container *ngIf="key.settings.icon || key.settings.safeCustomIcon">
|
||||
<mat-icon *ngIf="!key.settings.safeCustomIcon; else customToggleIcon">{{key.settings.icon}}</mat-icon>
|
||||
<ng-template #customToggleIcon>
|
||||
<img class="mat-icon" [src]="key.settings.safeCustomIcon" alt="icon">
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<span class="label-wrapper">{{key.label}}</span>
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
@ -64,3 +64,17 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host ::ng-deep {
|
||||
.tb-multiple-input {
|
||||
.mat-slide-toggle-content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.mat-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,14 @@ import { UtilsService } from '@core/services/utils.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DataKey, Datasource, DatasourceData, DatasourceType, WidgetConfig } from '@shared/models/widget.models';
|
||||
import { IWidgetSubscription } from '@core/api/widget-api.models';
|
||||
import { createLabelFromDatasource, isDefinedAndNotNull, isEqual, isNotEmptyStr, isUndefined } from '@core/utils';
|
||||
import {
|
||||
createLabelFromDatasource,
|
||||
isBoolean, isDefined,
|
||||
isDefinedAndNotNull,
|
||||
isEqual,
|
||||
isNotEmptyStr,
|
||||
isUndefined
|
||||
} from '@core/utils';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
import * as _moment from 'moment';
|
||||
import { FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
|
||||
@ -35,6 +42,7 @@ import { forkJoin, Observable, Subject } from 'rxjs';
|
||||
import { EntityId } from '@shared/models/id/entity-id';
|
||||
import { ResizeObserver } from '@juggle/resize-observer';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
||||
|
||||
type FieldAlignment = 'row' | 'column';
|
||||
|
||||
@ -44,6 +52,9 @@ type MultipleInputWidgetDataKeyValueType = 'string' | 'double' | 'integer' |
|
||||
'dateTime' | 'date' | 'time' | 'select';
|
||||
type MultipleInputWidgetDataKeyEditableType = 'editable' | 'disabled' | 'readonly';
|
||||
|
||||
type ConvertGetValueFunction = (value: any, ctx: WidgetContext) => any;
|
||||
type ConvertSetValueFunction = (value: any, originValue: any, ctx: WidgetContext) => any;
|
||||
|
||||
interface MultipleInputWidgetSettings {
|
||||
widgetTitle: string;
|
||||
showActionButtons: boolean;
|
||||
@ -66,6 +77,7 @@ interface MultipleInputWidgetSelectOption {
|
||||
interface MultipleInputWidgetDataKeySettings {
|
||||
dataKeyType: MultipleInputWidgetDataKeyType;
|
||||
dataKeyValueType: MultipleInputWidgetDataKeyValueType;
|
||||
slideToggleLabelPosition?: 'after' | 'before';
|
||||
selectOptions: MultipleInputWidgetSelectOption[];
|
||||
required: boolean;
|
||||
isEditable: MultipleInputWidgetDataKeyEditableType;
|
||||
@ -78,10 +90,19 @@ interface MultipleInputWidgetDataKeySettings {
|
||||
invalidDateErrorMessage?: string;
|
||||
minValueErrorMessage?: string;
|
||||
maxValueErrorMessage?: string;
|
||||
useCustomIcon: boolean;
|
||||
icon: string;
|
||||
customIcon: string ;
|
||||
safeCustomIcon?: SafeUrl;
|
||||
inputTypeNumber?: boolean;
|
||||
readOnly?: boolean;
|
||||
disabledOnCondition?: boolean;
|
||||
useGetValueFunction?: boolean;
|
||||
getValueFunctionBody?: string;
|
||||
getValueFunction?: ConvertGetValueFunction;
|
||||
useSetValueFunction?: boolean;
|
||||
setValueFunctionBody?: string;
|
||||
setValueFunction?: ConvertSetValueFunction;
|
||||
}
|
||||
|
||||
interface MultipleInputWidgetDataKey extends DataKey {
|
||||
@ -139,7 +160,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
|
||||
private utils: UtilsService,
|
||||
private fb: FormBuilder,
|
||||
private attributeService: AttributeService,
|
||||
private translate: TranslateService) {
|
||||
private translate: TranslateService,
|
||||
private sanitizer: DomSanitizer) {
|
||||
super(store);
|
||||
}
|
||||
|
||||
@ -265,6 +287,34 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
|
||||
});
|
||||
}
|
||||
|
||||
if (dataKey.settings.dataKeyValueType === 'booleanSwitch' && isUndefined(dataKey.settings.slideToggleLabelPosition)) {
|
||||
dataKey.settings.slideToggleLabelPosition = 'after';
|
||||
}
|
||||
|
||||
if (dataKey.settings.useCustomIcon && isDefinedAndNotNull(dataKey.settings.customIcon)) {
|
||||
dataKey.settings.safeCustomIcon = this.sanitizer.bypassSecurityTrustUrl(dataKey.settings.customIcon);
|
||||
}
|
||||
|
||||
if (dataKey.settings.useGetValueFunction && dataKey.settings.getValueFunctionBody.length) {
|
||||
try {
|
||||
dataKey.settings.getValueFunction =
|
||||
new Function('value, ctx', dataKey.settings.getValueFunctionBody) as ConvertGetValueFunction;
|
||||
} catch (e) {
|
||||
console.warn(`Parse getValue function in key ${dataKey.label}`, e);
|
||||
dataKey.settings.getValueFunction = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (dataKey.settings.useSetValueFunction && dataKey.settings.setValueFunctionBody.length) {
|
||||
try {
|
||||
dataKey.settings.setValueFunction =
|
||||
new Function('value, originValue, ctx', dataKey.settings.setValueFunctionBody) as ConvertSetValueFunction;
|
||||
} catch (e) {
|
||||
console.warn(`Parse setValue function in key ${dataKey.label}`, e);
|
||||
dataKey.settings.setValueFunction = null;
|
||||
}
|
||||
}
|
||||
|
||||
source.keys.push(dataKey);
|
||||
});
|
||||
} else {
|
||||
@ -342,28 +392,41 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
|
||||
source.keys.forEach((key) => {
|
||||
const keyData = data[dataIndex].data;
|
||||
if (keyData && keyData.length) {
|
||||
let value;
|
||||
let value = keyData[0][1];
|
||||
const keyValue = this.getKeyValue(value, key.settings);
|
||||
switch (key.settings.dataKeyValueType) {
|
||||
case 'dateTime':
|
||||
case 'date':
|
||||
if (isDefinedAndNotNull(keyData[0][1]) && keyData[0][1] !== '') {
|
||||
value = _moment(keyData[0][1]).toDate();
|
||||
if (isDefinedAndNotNull(keyValue) && keyValue !== '') {
|
||||
if (keyValue instanceof Date) {
|
||||
value = keyValue;
|
||||
} else {
|
||||
value = _moment(keyValue).toDate();
|
||||
}
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
break;
|
||||
case 'time':
|
||||
value = _moment().startOf('day').add(keyData[0][1], 'ms').toDate();
|
||||
if (keyValue instanceof Date) {
|
||||
value = keyValue;
|
||||
} else {
|
||||
value = _moment().startOf('day').add(keyValue, 'ms').toDate();
|
||||
}
|
||||
break;
|
||||
case 'booleanCheckbox':
|
||||
case 'booleanSwitch':
|
||||
value = (keyData[0][1] === 'true');
|
||||
if (isBoolean(keyValue)) {
|
||||
value = keyValue;
|
||||
} else {
|
||||
value = (keyValue === 'true');
|
||||
}
|
||||
break;
|
||||
case 'select':
|
||||
value = keyData[0][1].toString();
|
||||
value = keyValue.toString();
|
||||
break;
|
||||
default:
|
||||
value = keyData[0][1];
|
||||
value = keyValue;
|
||||
}
|
||||
key.value = value;
|
||||
}
|
||||
@ -399,6 +462,18 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
|
||||
});
|
||||
}
|
||||
|
||||
private getKeyValue(data: any, keySetting: MultipleInputWidgetDataKeySettings) {
|
||||
if (isDefined(keySetting.getValueFunction)) {
|
||||
try {
|
||||
return keySetting.getValueFunction(data, this.ctx);
|
||||
} catch (e) {
|
||||
console.warn(`Call function getValue`, e);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private updateWidgetDisplaying() {
|
||||
this.changeAlignment = (this.ctx.$container && this.ctx.$container[0].offsetWidth < 620);
|
||||
}
|
||||
@ -493,7 +568,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
|
||||
if (!this.settings.showActionButtons && !this.isSavingInProgress) {
|
||||
this.isSavingInProgress = true;
|
||||
const currentValue = this.multipleInputFormGroup.get(key.formId).value;
|
||||
if (!key.settings.required || (key.settings.required && isDefinedAndNotNull(currentValue) && isNotEmptyStr(currentValue.toString()))) {
|
||||
if (!key.settings.required ||
|
||||
(key.settings.required && isDefinedAndNotNull(currentValue) && isNotEmptyStr(currentValue.toString()))) {
|
||||
const dataToSave: MultipleInputWidgetSource = {
|
||||
datasource: source.datasource,
|
||||
keys: [key]
|
||||
@ -523,8 +599,9 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
|
||||
const sharedAttributes: AttributeData[] = [];
|
||||
const telemetry: AttributeData[] = [];
|
||||
for (const key of toSave.keys) {
|
||||
const currentValue = key.settings.dataKeyHidden ? key.value : this.multipleInputFormGroup.get(key.formId).value;
|
||||
let currentValue = key.settings.dataKeyHidden ? key.value : this.multipleInputFormGroup.get(key.formId).value;
|
||||
if (!isEqual(currentValue, key.value) || this.settings.updateAllValues) {
|
||||
currentValue = this.setKeyValue(currentValue, key, toSave);
|
||||
const attribute: AttributeData = {
|
||||
key: key.name,
|
||||
value: null
|
||||
@ -615,6 +692,21 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
|
||||
}
|
||||
}
|
||||
|
||||
private setKeyValue(value: any, key: MultipleInputWidgetDataKey, source: MultipleInputWidgetSource) {
|
||||
if (isDefined(key.settings.setValueFunction)) {
|
||||
const currentDatasourceData = this.subscription.data
|
||||
.find(dsData => dsData.datasource === source.datasource && dsData.dataKey.name === key.name);
|
||||
const originValue = currentDatasourceData.data[0][1];
|
||||
try {
|
||||
return key.settings.setValueFunction(value, originValue, this.ctx);
|
||||
} catch (e) {
|
||||
console.warn(`Call function setValue`, e);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public discardAll() {
|
||||
this.multipleInputFormGroup.reset(undefined, {emitEvent: false});
|
||||
this.sources.forEach((source) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user