Browse Source

Merge pull request #3561 from ChantsovaEkaterina/improvement/extend-multiple-input-settings

Multiple input widget: extend settings
pull/3574/head
Igor Kulikov 6 years ago
committed by GitHub
parent
commit
cb9f5cde85
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      application/src/main/data/json/system/widget_bundles/input_widgets.json
  2. 57
      ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html
  3. 22
      ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.scss
  4. 111
      ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts
  5. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json

4
application/src/main/data/json/system/widget_bundles/input_widgets.json

File diff suppressed because one or more lines are too long

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

@ -39,7 +39,7 @@
(blur)="key.isFocused = false; inputChanged(source, key)">
<mat-icon *ngIf="key.settings.icon" matPrefix>{{key.settings.icon}}</mat-icon>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
{{key.settings.requiredErrorMessage}}
{{ getErrorMessageText(key.settings, 'required') }}
</mat-error>
</mat-form-field>
</div>
@ -53,47 +53,56 @@
[readonly]="key.settings.isEditable === 'readonly'"
type="number"
step="{{key.settings.step}}"
min="{{key.settings.minValue}}"
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>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
{{key.settings.requiredErrorMessage}}
{{ getErrorMessageText(key.settings,'required') }}
</mat-error>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('min')">
{{ getErrorMessageText(key.settings,'min') }}
</mat-error>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('max')">
{{ getErrorMessageText(key.settings,'max') }}
</mat-error>
</mat-form-field>
</div>
<div class="input-field mat-block" *ngIf="key.settings.dataKeyValueType === 'booleanCheckbox'">
<mat-checkbox formControlName="{{key.formId}}"
(change)="inputChanged(source, key)">
{{key.label}}
<span class="label-wrapper">{{key.label}}</span>
</mat-checkbox>
</div>
<div class="input-field mat-block" *ngIf="key.settings.dataKeyValueType === 'booleanSwitch'">
<mat-slide-toggle formControlName="{{key.formId}}"
(change)="inputChanged(source, key)">
{{key.label}}
<span class="label-wrapper">{{key.label}}</span>
</mat-slide-toggle>
</div>
<div class="input-field mat-block date-time-input" *ngIf="(key.settings.dataKeyValueType === 'dateTime') ||
(key.settings.dataKeyValueType === 'date') ||
(key.settings.dataKeyValueType === 'time')" fxLayout="column">
<div fxLayout="row" [ngClass]="{'vertically-aligned': smallWidthContainer}" fxLayoutGap="16px">
<mat-form-field>
<mat-placeholder>{{key.label}}</mat-placeholder>
<mat-datetimepicker-toggle [for]="datePicker" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #datePicker type="{{datePickerType(key.settings.dataKeyValueType)}}"
openOnFocus="true"></mat-datetimepicker>
<input matInput formControlName="{{key.formId}}"
[required]="key.settings.required"
[readonly]="key.settings.isEditable === 'readonly'"
[matDatetimepicker]="datePicker"
(focus)="key.isFocused = true;"
(blur)="key.isFocused = false;"
(dateChange)="inputChanged(source, key)">
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
{{key.settings.requiredErrorMessage}}
</mat-error>
</mat-form-field>
</div>
<mat-form-field>
<mat-placeholder>{{key.label}}</mat-placeholder>
<mat-datetimepicker-toggle [for]="datePicker" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #datePicker type="{{datePickerType(key.settings.dataKeyValueType)}}"
openOnFocus="true"></mat-datetimepicker>
<input matInput formControlName="{{key.formId}}"
[required]="key.settings.required"
[readonly]="key.settings.isEditable === 'readonly'"
[matDatetimepicker]="datePicker"
(focus)="key.isFocused = true;"
(blur)="key.isFocused = false;"
(dateChange)="inputChanged(source, key)">
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
{{ getErrorMessageText(key.settings, 'required') }}
</mat-error>
<mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('matDatepickerParse')">
{{ getErrorMessageText(key.settings, 'invalidDate') }}
</mat-error>
</mat-form-field>
</div>
</div>
</div>
@ -103,12 +112,12 @@
<button mat-button color="primary" type="button"
(click)="discardAll()" style="max-height: 50px; margin-right:20px;"
[disabled]="!multipleInputFormGroup.dirty">
{{ 'action.undo' | translate }}
{{ resetButtonLabel }}
</button>
<button mat-button mat-raised-button color="primary" type="submit"
style="max-height: 50px; margin-right:20px;"
[disabled]="!multipleInputFormGroup.dirty || multipleInputFormGroup.invalid">
{{ 'action.save' | translate }}
{{ saveButtonLabel }}
</button>
</div>
</div>

22
ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.scss

@ -44,7 +44,13 @@
display: block;
margin-top: 20px;
margin-bottom: 16px;
white-space: normal;
.label-wrapper {
white-space: normal;
}
}
mat-slide-toggle {
height: auto;
}
.date-time-input {
@ -56,20 +62,6 @@
.vertical-alignment {
flex-direction: column;
mat-checkbox,
mat-slide-toggle {
margin-top: 18px;
}
mat-slide-toggle {
display: flex;
justify-content: space-between;
}
}
.vertically-aligned {
flex-direction: column;
}
}
}

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

@ -24,7 +24,7 @@ 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 { isDefined, isEqual, isUndefined, createLabelFromDatasource } from '@core/utils';
import { isDefined, isEqual, isUndefined, createLabelFromDatasource, isDefinedAndNotNull } from '@core/utils';
import { EntityType } from '@shared/models/entity-type.models';
import * as _moment from 'moment';
import { FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
@ -47,6 +47,8 @@ interface MultipleInputWidgetSettings {
widgetTitle: string;
showActionButtons: boolean;
updateAllValues: boolean;
saveButtonLabel: string;
resetButtonLabel: string;
showResultMessage: boolean;
showGroupTitle: boolean;
groupTitle: string;
@ -62,8 +64,13 @@ interface MultipleInputWidgetDataKeySettings {
isEditable: MultipleInputWidgetDataKeyEditableType;
disabledOnDataKey: string;
dataKeyHidden: boolean;
step: number;
requiredErrorMessage: string;
step?: number;
minValue?: number;
maxValue?: number;
requiredErrorMessage?: string;
invalidDateErrorMessage?: string;
minValueErrorMessage?: string;
maxValueErrorMessage?: string;
icon: string;
inputTypeNumber?: boolean;
readOnly?: boolean;
@ -104,7 +111,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
isVerticalAlignment: boolean;
inputWidthSettings: string;
changeAlignment: boolean;
smallWidthContainer: boolean;
saveButtonLabel: string;
resetButtonLabel: string;
entityDetected = false;
isAllParametersValid = true;
@ -157,6 +165,17 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
this.settings.groupTitle = this.settings.groupTitle || '${entityName}';
if (this.settings.saveButtonLabel && this.settings.saveButtonLabel.length) {
this.saveButtonLabel = this.utils.customTranslation(this.settings.saveButtonLabel, this.settings.saveButtonLabel);
} else {
this.saveButtonLabel = this.translate.instant('action.save');
}
if (this.settings.resetButtonLabel && this.settings.resetButtonLabel.length) {
this.resetButtonLabel = this.utils.customTranslation(this.settings.resetButtonLabel, this.settings.resetButtonLabel);
} else {
this.resetButtonLabel = this.translate.instant('action.undo');
}
// For backward compatibility
if (isUndefined(this.settings.showActionButtons)) {
this.settings.showActionButtons = true;
@ -195,7 +214,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
if (dataKey.units) {
dataKey.label += ' (' + dataKey.units + ')';
}
dataKey.formId = (++keyIndex)+'';
dataKey.formId = (++keyIndex) + '';
dataKey.isFocused = false;
// For backward compatibility
@ -245,6 +264,14 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
if (key.settings.dataKeyValueType === 'integer') {
validators.push(Validators.pattern(/^-?[0-9]+$/));
}
if (key.settings.dataKeyValueType === 'integer' || key.settings.dataKeyValueType === 'double') {
if (isDefinedAndNotNull(key.settings.minValue) && isFinite(key.settings.minValue)) {
validators.push(Validators.min(key.settings.minValue));
}
if (isDefinedAndNotNull(key.settings.maxValue) && isFinite(key.settings.maxValue)) {
validators.push(Validators.max(key.settings.maxValue));
}
}
const formControl = this.fb.control(
{ value: key.value,
disabled: key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition},
@ -265,7 +292,11 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
switch (key.settings.dataKeyValueType) {
case 'dateTime':
case 'date':
value = _moment(keyData[0][1]).toDate();
if (isDefinedAndNotNull(keyData[0][1]) && keyData[0][1] !== '') {
value = _moment(keyData[0][1]).toDate();
} else {
value = null;
}
break;
case 'time':
value = _moment().startOf('day').add(keyData[0][1], 'ms').toDate();
@ -313,25 +344,69 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
private updateWidgetDisplaying() {
this.changeAlignment = (this.ctx.$container && this.ctx.$container[0].offsetWidth < 620);
this.smallWidthContainer = (this.ctx.$container && this.ctx.$container[0].offsetWidth < 420);
}
public onDataUpdated() {
this.ngZone.run(() => {
this.updateWidgetData(this.subscription.data);
this.ctx.detectChanges();
});
this.updateWidgetData(this.subscription.data);
this.ctx.detectChanges();
}
private resize() {
this.ngZone.run(() => {
this.updateWidgetDisplaying();
this.ctx.detectChanges();
});
this.updateWidgetDisplaying();
this.ctx.detectChanges();
}
public getGroupTitle(datasource: Datasource): string {
return createLabelFromDatasource(datasource, this.settings.groupTitle);
const groupTitle = createLabelFromDatasource(datasource, this.settings.groupTitle);
return this.utils.customTranslation(groupTitle, groupTitle);
}
public getErrorMessageText(keySettings: MultipleInputWidgetDataKeySettings, errorType: string): string {
let errorMessage;
let defaultMessage;
let messageValues;
switch (errorType) {
case 'required':
errorMessage = keySettings.requiredErrorMessage;
defaultMessage = '';
break;
case 'min':
errorMessage = keySettings.minValueErrorMessage;
defaultMessage = 'widgets.input-widgets.min-value-error';
messageValues = {
value: keySettings.minValue
};
break;
case 'max':
errorMessage = keySettings.maxValueErrorMessage;
defaultMessage = 'widgets.input-widgets.max-value-error';
messageValues = {
value: keySettings.maxValue
};
break;
case 'invalidDate':
errorMessage = keySettings.invalidDateErrorMessage;
defaultMessage = 'widgets.input-widgets.invalid-date';
break;
default:
return '';
}
return this.getTranslatedErrorText(errorMessage, defaultMessage, messageValues);
}
public getTranslatedErrorText(errorMessage: string, defaultMessage: string, messageValues?: object): string {
let messageText;
if (errorMessage && errorMessage.length) {
messageText = this.utils.customTranslation(errorMessage, errorMessage);
} else if (defaultMessage && defaultMessage.length) {
if (!messageValues) {
messageValues = {};
}
messageText = this.translate.instant(defaultMessage, messageValues);
} else {
messageText = '';
}
return messageText;
}
public visibleKeys(source: MultipleInputWidgetSource): MultipleInputWidgetDataKey[] {
@ -455,8 +530,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
if (tasks.length) {
forkJoin(tasks).subscribe(
() => {
this.multipleInputFormGroup.reset(undefined, {emitEvent: false});
this.multipleInputFormGroup.markAsPristine();
this.ctx.detectChanges();
if (this.settings.showResultMessage) {
this.ctx.showSuccessToast(this.translate.instant('widgets.input-widgets.update-successful'),
1000, 'bottom', 'left', this.toastTargetId);
@ -469,8 +544,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
}
});
} else {
this.multipleInputFormGroup.reset(undefined, {emitEvent: false});
this.multipleInputFormGroup.markAsPristine();
this.ctx.detectChanges();
}
}

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

@ -2187,8 +2187,11 @@
"entity-coordinate-required": "Both fields, latitude and longitude, are required",
"entity-timeseries-required": "Entity timeseries is required",
"get-location": "Get current location",
"invalid-date": "Invalid Date",
"latitude": "Latitude",
"longitude": "Longitude",
"min-value-error": "Min value is {{value}}",
"max-value-error": "Max value is {{value}}",
"not-allowed-entity": "Selected entity cannot have shared attributes",
"no-attribute-selected": "No attribute is selected",
"no-datakey-selected": "No datakey is selected",

Loading…
Cancel
Save