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

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

@ -44,7 +44,13 @@
display: block; display: block;
margin-top: 20px; margin-top: 20px;
margin-bottom: 16px; margin-bottom: 16px;
white-space: normal; .label-wrapper {
white-space: normal;
}
}
mat-slide-toggle {
height: auto;
} }
.date-time-input { .date-time-input {
@ -56,20 +62,6 @@
.vertical-alignment { .vertical-alignment {
flex-direction: column; 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 { TranslateService } from '@ngx-translate/core';
import { DataKey, Datasource, DatasourceData, DatasourceType, WidgetConfig } from '@shared/models/widget.models'; import { DataKey, Datasource, DatasourceData, DatasourceType, WidgetConfig } from '@shared/models/widget.models';
import { IWidgetSubscription } from '@core/api/widget-api.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 { EntityType } from '@shared/models/entity-type.models';
import * as _moment from 'moment'; import * as _moment from 'moment';
import { FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
@ -47,6 +47,8 @@ interface MultipleInputWidgetSettings {
widgetTitle: string; widgetTitle: string;
showActionButtons: boolean; showActionButtons: boolean;
updateAllValues: boolean; updateAllValues: boolean;
saveButtonLabel: string;
resetButtonLabel: string;
showResultMessage: boolean; showResultMessage: boolean;
showGroupTitle: boolean; showGroupTitle: boolean;
groupTitle: string; groupTitle: string;
@ -62,8 +64,13 @@ interface MultipleInputWidgetDataKeySettings {
isEditable: MultipleInputWidgetDataKeyEditableType; isEditable: MultipleInputWidgetDataKeyEditableType;
disabledOnDataKey: string; disabledOnDataKey: string;
dataKeyHidden: boolean; dataKeyHidden: boolean;
step: number; step?: number;
requiredErrorMessage: string; minValue?: number;
maxValue?: number;
requiredErrorMessage?: string;
invalidDateErrorMessage?: string;
minValueErrorMessage?: string;
maxValueErrorMessage?: string;
icon: string; icon: string;
inputTypeNumber?: boolean; inputTypeNumber?: boolean;
readOnly?: boolean; readOnly?: boolean;
@ -104,7 +111,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
isVerticalAlignment: boolean; isVerticalAlignment: boolean;
inputWidthSettings: string; inputWidthSettings: string;
changeAlignment: boolean; changeAlignment: boolean;
smallWidthContainer: boolean; saveButtonLabel: string;
resetButtonLabel: string;
entityDetected = false; entityDetected = false;
isAllParametersValid = true; isAllParametersValid = true;
@ -157,6 +165,17 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
this.settings.groupTitle = this.settings.groupTitle || '${entityName}'; 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 // For backward compatibility
if (isUndefined(this.settings.showActionButtons)) { if (isUndefined(this.settings.showActionButtons)) {
this.settings.showActionButtons = true; this.settings.showActionButtons = true;
@ -195,7 +214,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
if (dataKey.units) { if (dataKey.units) {
dataKey.label += ' (' + dataKey.units + ')'; dataKey.label += ' (' + dataKey.units + ')';
} }
dataKey.formId = (++keyIndex)+''; dataKey.formId = (++keyIndex) + '';
dataKey.isFocused = false; dataKey.isFocused = false;
// For backward compatibility // For backward compatibility
@ -245,6 +264,14 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
if (key.settings.dataKeyValueType === 'integer') { if (key.settings.dataKeyValueType === 'integer') {
validators.push(Validators.pattern(/^-?[0-9]+$/)); 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( const formControl = this.fb.control(
{ value: key.value, { value: key.value,
disabled: key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition}, disabled: key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition},
@ -265,7 +292,11 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
switch (key.settings.dataKeyValueType) { switch (key.settings.dataKeyValueType) {
case 'dateTime': case 'dateTime':
case 'date': 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; break;
case 'time': case 'time':
value = _moment().startOf('day').add(keyData[0][1], 'ms').toDate(); value = _moment().startOf('day').add(keyData[0][1], 'ms').toDate();
@ -313,25 +344,69 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
private updateWidgetDisplaying() { private updateWidgetDisplaying() {
this.changeAlignment = (this.ctx.$container && this.ctx.$container[0].offsetWidth < 620); this.changeAlignment = (this.ctx.$container && this.ctx.$container[0].offsetWidth < 620);
this.smallWidthContainer = (this.ctx.$container && this.ctx.$container[0].offsetWidth < 420);
} }
public onDataUpdated() { public onDataUpdated() {
this.ngZone.run(() => { this.updateWidgetData(this.subscription.data);
this.updateWidgetData(this.subscription.data); this.ctx.detectChanges();
this.ctx.detectChanges();
});
} }
private resize() { private resize() {
this.ngZone.run(() => { this.updateWidgetDisplaying();
this.updateWidgetDisplaying(); this.ctx.detectChanges();
this.ctx.detectChanges();
});
} }
public getGroupTitle(datasource: Datasource): string { 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[] { public visibleKeys(source: MultipleInputWidgetSource): MultipleInputWidgetDataKey[] {
@ -455,8 +530,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
if (tasks.length) { if (tasks.length) {
forkJoin(tasks).subscribe( forkJoin(tasks).subscribe(
() => { () => {
this.multipleInputFormGroup.reset(undefined, {emitEvent: false});
this.multipleInputFormGroup.markAsPristine(); this.multipleInputFormGroup.markAsPristine();
this.ctx.detectChanges();
if (this.settings.showResultMessage) { if (this.settings.showResultMessage) {
this.ctx.showSuccessToast(this.translate.instant('widgets.input-widgets.update-successful'), this.ctx.showSuccessToast(this.translate.instant('widgets.input-widgets.update-successful'),
1000, 'bottom', 'left', this.toastTargetId); 1000, 'bottom', 'left', this.toastTargetId);
@ -469,8 +544,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
} }
}); });
} else { } else {
this.multipleInputFormGroup.reset(undefined, {emitEvent: false});
this.multipleInputFormGroup.markAsPristine(); 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-coordinate-required": "Both fields, latitude and longitude, are required",
"entity-timeseries-required": "Entity timeseries is required", "entity-timeseries-required": "Entity timeseries is required",
"get-location": "Get current location", "get-location": "Get current location",
"invalid-date": "Invalid Date",
"latitude": "Latitude", "latitude": "Latitude",
"longitude": "Longitude", "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", "not-allowed-entity": "Selected entity cannot have shared attributes",
"no-attribute-selected": "No attribute is selected", "no-attribute-selected": "No attribute is selected",
"no-datakey-selected": "No datakey is selected", "no-datakey-selected": "No datakey is selected",

Loading…
Cancel
Save