Browse Source

Merge pull request #5311 from Dmitriymush/feature/update_json_attribute

Feature/funtionality to handle JSON attribute in edit multiple attributes widget
pull/8363/head
Igor Kulikov 4 years ago
committed by GitHub
parent
commit
e8f35120ba
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 33
      ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html
  2. 68
      ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts
  3. 25
      ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html
  4. 26
      ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts
  5. 4
      ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html
  6. 32
      ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts
  7. 1
      ui-ngx/src/app/shared/shared.module.ts
  8. 8
      ui-ngx/src/assets/locale/locale.constant-en_US.json

33
ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html

@ -85,6 +85,39 @@
<span class="label-wrapper">{{key.label}}</span>
</mat-checkbox>
</div>
<div class="input-field" *ngIf="key.settings.dataKeyValueType === 'JSON'">
<mat-form-field class="mat-block">
<mat-label>{{key.label}}</mat-label>
<input
matInput
type="text"
formControlName="{{key.formId}}"
tb-json-to-string
[readonly]="key.settings.isEditable === 'readonly'"
[required]="key.settings.required"
(focus)="key.isFocused = true; focusInputElement($event)"
(blur)="key.isFocused = false; inputChanged(source, key)"
/>
<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>
<button [disabled]="key.settings.isEditable === 'disabled' || key.settings.isEditable === 'readonly'"
type="button"
matSuffix mat-icon-button
(click)="openEditJSONDialog($event, key, source)">
<mat-icon>open_in_new</mat-icon>
</button>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
{{ getErrorMessageText(key.settings,'required') }}
</mat-error>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('invalidJSON')">
{{ getErrorMessageText(key.settings,'invalidJSON') | translate }}
</mat-error>
</mat-form-field>
</div>
<div class="input-field mat-block" *ngIf="key.settings.dataKeyValueType === 'booleanSwitch'">
<mat-slide-toggle formControlName="{{key.formId}}"
[labelPosition]="key.settings.slideToggleLabelPosition"

68
ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts

@ -26,10 +26,10 @@ import { DataKey, Datasource, DatasourceData, DatasourceType, WidgetConfig } fro
import { IWidgetSubscription } from '@core/api/widget-api.models';
import {
createLabelFromDatasource,
isBoolean, isDefined,
isBoolean,
isDefined,
isDefinedAndNotNull,
isEqual,
isNotEmptyStr,
isUndefined
} from '@core/utils';
import { EntityType } from '@shared/models/entity-type.models';
@ -42,14 +42,19 @@ 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 {
JsonObjectEditDialogComponent,
JsonObjectEditDialogData
} from '@shared/components/dialog/json-object-edit-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
type FieldAlignment = 'row' | 'column';
type MultipleInputWidgetDataKeyType = 'server' | 'shared' | 'timeseries';
type MultipleInputWidgetDataKeyValueType = 'string' | 'double' | 'integer' |
'booleanCheckbox' | 'booleanSwitch' |
'dateTime' | 'date' | 'time' | 'select';
export type MultipleInputWidgetDataKeyValueType = 'string' | 'double' | 'integer' |
'JSON' | 'booleanCheckbox' | 'booleanSwitch' |
'dateTime' | 'date' | 'time' | 'select';
type MultipleInputWidgetDataKeyEditableType = 'editable' | 'disabled' | 'readonly';
type ConvertGetValueFunction = (value: any, ctx: WidgetContext) => any;
@ -90,6 +95,7 @@ interface MultipleInputWidgetDataKeySettings {
invalidDateErrorMessage?: string;
minValueErrorMessage?: string;
maxValueErrorMessage?: string;
invalidJsonErrorMessage?: string;
useCustomIcon: boolean;
icon: string;
customIcon: string ;
@ -103,6 +109,9 @@ interface MultipleInputWidgetDataKeySettings {
useSetValueFunction?: boolean;
setValueFunctionBody?: string;
setValueFunction?: ConvertSetValueFunction;
dialogTitle?: string;
saveButtonLabel?: string;
cancelButtonLabel?: string;
}
interface MultipleInputWidgetDataKey extends DataKey {
@ -161,7 +170,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
private fb: UntypedFormBuilder,
private attributeService: AttributeService,
private translate: TranslateService,
private sanitizer: DomSanitizer) {
private sanitizer: DomSanitizer,
private dialog: MatDialog) {
super(store);
}
@ -425,6 +435,13 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
case 'select':
value = keyValue !== null ? keyValue.toString() : null;
break;
case 'JSON':
try {
value = JSON.parse(keyValue);
} catch (e) {
value = keyValue ? keyValue : null;
}
break;
default:
value = keyValue;
}
@ -520,6 +537,10 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
errorMessage = keySettings.invalidDateErrorMessage;
defaultMessage = 'widgets.input-widgets.invalid-date';
break;
case 'invalidJSON':
errorMessage = keySettings.invalidJsonErrorMessage;
defaultMessage = 'widgets.input-widgets.json-invalid';
break;
default:
return '';
}
@ -670,8 +691,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
}
});
if (tasks.length) {
forkJoin(tasks).subscribe(
() => {
forkJoin(tasks).subscribe({
next: () => {
this.multipleInputFormGroup.markAsPristine();
this.ctx.detectChanges();
this.isSavingInProgress = false;
@ -680,13 +701,13 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
1000, 'bottom', 'left', this.toastTargetId);
}
},
() => {
error: () => {
this.isSavingInProgress = false;
if (this.settings.showResultMessage) {
this.ctx.showErrorToast(this.translate.instant('widgets.input-widgets.update-failed'),
'bottom', 'left', this.toastTargetId);
}
});
}});
} else {
this.multipleInputFormGroup.markAsPristine();
this.ctx.detectChanges();
@ -718,4 +739,31 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
});
this.multipleInputFormGroup.markAsPristine();
}
openEditJSONDialog($event: Event, key: MultipleInputWidgetDataKey, source: MultipleInputWidgetSource) {
if ($event) {
$event.stopPropagation();
}
const formControl = this.multipleInputFormGroup.controls[key.formId];
this.dialog.open<JsonObjectEditDialogComponent, JsonObjectEditDialogData, object>(JsonObjectEditDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
jsonValue: formControl.value,
title: key.settings.dialogTitle,
saveLabel: key.settings.saveButtonLabel,
cancelLabel: key.settings.cancelButtonLabel
}
}).afterClosed().subscribe(
(res) => {
if (!isEqual(res, formControl.value)) {
formControl.patchValue(res);
formControl.markAsDirty();
if(!this.settings.showActionButtons) {
this.inputChanged(source, key);
}
}
}
);
}
}

25
ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html

@ -66,6 +66,9 @@
<mat-option [value]="'select'">
{{ 'widgets.input-widgets.datakey-value-type-select' | translate }}
</mat-option>
<mat-option [value]="'JSON'">
{{ 'widgets.input-widgets.datakey-value-type-json' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</section>
@ -179,6 +182,28 @@
<mat-label translate>widgets.input-widgets.invalid-date-error-message</mat-label>
<input matInput formControlName="invalidDateErrorMessage">
</mat-form-field>
<mat-form-field [fxShow]="updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value === 'JSON'" fxFlex class="mat-block">
<mat-label translate>widgets.input-widgets.invalid-JSON-error-message</mat-label>
<input matInput formControlName="invalidJsonErrorMessage">
</mat-form-field>
</fieldset>
<fieldset [fxShow]="!updateMultipleAttributesKeySettingsForm.get('dataKeyHidden').value &&
updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value === 'JSON'" class="fields-group">
<legend class="group-title" translate>widgets.input-widgets.dialog-editor-settings</legend>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.input-widgets.title</mat-label>
<input matInput formControlName="dialogTitle">
</mat-form-field>
<section fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" fxLayoutAlign.gt-xs="start center">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.input-widgets.save-button-label</mat-label>
<input matInput formControlName="saveButtonLabel">
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.input-widgets.cancel-button-label</mat-label>
<input matInput formControlName="cancelButtonLabel">
</mat-form-field>
</section>
</fieldset>
<fieldset [fxShow]="!updateMultipleAttributesKeySettingsForm.get('dataKeyHidden').value" class="fields-group">
<legend class="group-title" translate>widgets.input-widgets.icon-settings</legend>

26
ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts

@ -24,6 +24,7 @@ import {
dataKeySelectOptionValidator
} from '@home/components/widget/lib/settings/input/datakey-select-option.component';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { MultipleInputWidgetDataKeyValueType } from '@home/components/widget/lib/multiple-input-widget.component';
@Component({
selector: 'tb-update-multiple-attributes-key-settings',
@ -62,6 +63,11 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings
minValueErrorMessage: '',
maxValueErrorMessage: '',
invalidDateErrorMessage: '',
invalidJsonErrorMessage: '',
dialogTitle: '',
saveButtonLabel: '',
cancelButtonLabel: '',
useCustomIcon: false,
icon: '',
@ -107,6 +113,13 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings
minValueErrorMessage: [settings.minValueErrorMessage, []],
maxValueErrorMessage: [settings.maxValueErrorMessage, []],
invalidDateErrorMessage: [settings.invalidDateErrorMessage, []],
invalidJsonErrorMessage: [settings.invalidJsonErrorMessage, []],
// Dialog settings
dialogTitle: [settings.dialogTitle, []],
saveButtonLabel: [settings.saveButtonLabel, []],
cancelButtonLabel: [settings.cancelButtonLabel, []],
// Icon settings
@ -130,7 +143,8 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings
protected updateValidators(emitEvent: boolean) {
const dataKeyHidden: boolean = this.updateMultipleAttributesKeySettingsForm.get('dataKeyHidden').value;
const dataKeyValueType: string = this.updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value;
const dataKeyValueType: MultipleInputWidgetDataKeyValueType =
this.updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value;
const required: boolean = this.updateMultipleAttributesKeySettingsForm.get('required').value;
const isEditable: string = this.updateMultipleAttributesKeySettingsForm.get('isEditable').value;
const useCustomIcon: boolean = this.updateMultipleAttributesKeySettingsForm.get('useCustomIcon').value;
@ -165,6 +179,11 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings
this.updateMultipleAttributesKeySettingsForm.get('maxValueErrorMessage').enable({emitEvent: false});
} else if (dataKeyValueType === 'dateTime' || dataKeyValueType === 'date' || dataKeyValueType === 'time') {
this.updateMultipleAttributesKeySettingsForm.get('invalidDateErrorMessage').enable({emitEvent: false});
} else if (dataKeyValueType === 'JSON') {
this.updateMultipleAttributesKeySettingsForm.get('invalidJsonErrorMessage').enable({emitEvent: false});
this.updateMultipleAttributesKeySettingsForm.get('dialogTitle').enable({emitEvent: false});
this.updateMultipleAttributesKeySettingsForm.get('saveButtonLabel').enable({emitEvent: false});
this.updateMultipleAttributesKeySettingsForm.get('cancelButtonLabel').enable({emitEvent: false});
}
if (required) {
this.updateMultipleAttributesKeySettingsForm.get('requiredErrorMessage').enable({emitEvent: false});
@ -243,8 +262,9 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings
displayErrorMessagesSection(): boolean {
const dataKeyHidden: boolean = this.updateMultipleAttributesKeySettingsForm.get('dataKeyHidden').value;
const required: boolean = this.updateMultipleAttributesKeySettingsForm.get('required').value;
const dataKeyValueType: string = this.updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value;
return !dataKeyHidden && (required || (['integer', 'double', 'dateTime', 'date', 'time'].includes(dataKeyValueType)));
const dataKeyValueType: MultipleInputWidgetDataKeyValueType =
this.updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value;
return !dataKeyHidden && (required || (['integer', 'double', 'dateTime', 'date', 'time', 'JSON'].includes(dataKeyValueType)));
}
}

4
ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html

@ -45,12 +45,12 @@
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }}
{{ cancelButtonLabel }}
</button>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || jsonFormGroup.invalid || !jsonFormGroup.dirty">
{{ 'action.save' | translate }}
{{ saveButtonLabel }}
</button>
</div>
</form>

32
ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts

@ -14,18 +14,21 @@
/// limitations under the License.
///
import { Component, Inject, OnInit } from '@angular/core';
import { Component, Inject } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { FormBuilder, FormGroup } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { isNotEmptyStr } from '@core/utils';
export interface JsonObjectEditDialogData {
jsonValue: object;
title?: string;
saveLabel?: string;
cancelLabel?: string;
}
@Component({
@ -33,24 +36,29 @@ export interface JsonObjectEditDialogData {
templateUrl: './json-object-edit-dialog.component.html',
styleUrls: []
})
export class JsonObjectEditDialogComponent extends DialogComponent<JsonObjectEditDialogComponent, object> implements OnInit {
export class JsonObjectEditDialogComponent extends DialogComponent<JsonObjectEditDialogComponent, object> {
jsonFormGroup: UntypedFormGroup;
title: string;
submitted = false;
jsonFormGroup: FormGroup;
title = this.translate.instant('details.edit-json');
saveButtonLabel = this.translate.instant('action.save');
cancelButtonLabel = this.translate.instant('action.cancel');
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: JsonObjectEditDialogData,
public dialogRef: MatDialogRef<JsonObjectEditDialogComponent, object>,
public fb: UntypedFormBuilder,
public fb: FormBuilder,
private translate: TranslateService) {
super(store, router, dialogRef);
}
ngOnInit(): void {
this.title = this.data.title ? this.data.title : this.translate.instant('details.edit-json');
if (isNotEmptyStr(this.data.title)) {
this.title = this.data.title;
}
if (isNotEmptyStr(this.data.saveLabel)) {
this.saveButtonLabel = this.data.saveLabel;
}
if (isNotEmptyStr(this.data.cancelLabel)) {
this.cancelButtonLabel = this.data.cancelLabel;
}
this.jsonFormGroup = this.fb.group({
json: [this.data.jsonValue, []]
});

1
ui-ngx/src/app/shared/shared.module.ts

@ -464,6 +464,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
HtmlComponent,
FabTriggerDirective,
FabActionsDirective,
TbJsonToStringDirective,
FabToolbarComponent,
WidgetsBundleSelectComponent,
ValueInputComponent,

8
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -4597,6 +4597,7 @@
"datakey-value-type-string": "String",
"datakey-value-type-double": "Double",
"datakey-value-type-integer": "Integer",
"datakey-value-type-json": "JSON",
"datakey-value-type-boolean-checkbox": "Boolean (Checkbox)",
"datakey-value-type-boolean-switch": "Boolean (Switch)",
"datakey-value-type-date-time": "Date & Time",
@ -4622,7 +4623,9 @@
"min-value-error-message": "'Min value' error message",
"max-value-error-message": "'Max value' error message",
"invalid-date-error-message": "'Invalid date' error message",
"invalid-JSON-error-message": "'Invalid JSON' error message",
"icon-settings": "Icon settings",
"dialog-editor-settings": "Dialog editor settings",
"use-custom-icon": "Use custom icon",
"input-cell-icon": "Icon to show before input cell",
"value-conversion-settings": "Value conversion settings",
@ -4631,7 +4634,10 @@
"get-value-function": "getValue function",
"set-value-settings": "Set value settings",
"use-set-value-function": "Use setValue function",
"set-value-function": "setValue function"
"set-value-function": "setValue function",
"json-invalid": "JSON value has an invalid format",
"title": "Title",
"cancel-button-label": "'Cancel' button label"
},
"invalid-qr-code-text": "Invalid input text for QR code. Input should have a string type",
"qr-code": {

Loading…
Cancel
Save