diff --git a/application/src/main/data/json/system/widget_bundles/input_widgets.json b/application/src/main/data/json/system/widget_bundles/input_widgets.json
index 51717b12ec..7328dc7fb3 100644
--- a/application/src/main/data/json/system/widget_bundles/input_widgets.json
+++ b/application/src/main/data/json/system/widget_bundles/input_widgets.json
@@ -319,9 +319,9 @@
"resources": [],
"templateHtml": "\n",
"templateCss": "",
- "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet toast;\r\nlet utils;\r\nlet types;\r\n\r\nself.onInit = function() {\r\n var scope = self.ctx.$scope;\r\n var id = self.ctx.$scope.$injector.get('utils').guid();\r\n scope.formId = \"form-\"+id;\r\n scope.ctx = self.ctx;\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.$broadcast('multiple-input-data-updated', self.ctx.$scope.formId);\r\n}\r\n",
- "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"MultipleInput\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Multiple input title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"attributesShared\": {\n \"title\": \"Attributes are 'shared' (default value is 'server')\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\":true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"attributesShared\",\n \"showResultMessage\"\n ]\n}",
- "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"readOnly\": {\n \"title\": \"Value is read only\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"inputTypeNumber\": {\n \"title\": \"Datakey is a number\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"step\": {\n \"title\": \"Step interval between valid values (only for numbers)\",\n \"type\": \"number\",\n \"default\": \"1\"\n },\n \"icon\": {\n \"title\": \"Icon to show before input cell\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"readOnly\",\n \"inputTypeNumber\",\n \"step\",\n\t\t{\n \t\t\"key\": \"icon\",\n\t\t\t\"type\": \"icon\"\n\t\t},\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n }\n ]\n}\n",
+ "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet toast;\r\nlet utils;\r\nlet types;\r\n\r\nself.onInit = function() {\r\n var scope = self.ctx.$scope;\r\n var id = self.ctx.$scope.$injector.get('utils').guid();\r\n scope.formId = \"form-\"+id;\r\n scope.ctx = self.ctx;\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.$broadcast('multiple-input-data-updated', self.ctx.$scope.formId);\r\n}\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1\r\n }\r\n}\r\n\r\nself.onResize = function() {\r\n self.ctx.$scope.$broadcast('multiple-input-resize', self.ctx.$scope.formId);\r\n}\r\n",
+ "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"MultipleInput\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showActionButtons\":{\n \"title\":\"Show action buttons\",\n \"type\":\"boolean\",\n \"default\": true\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\": true\n },\n \"fieldsAlignment\": {\n \"title\": \"Fields alignment\",\n \"type\": \"string\",\n \"default\": \"row\"\n },\n \"fieldsInRow\": {\n \"title\": \"Number of fields in the row\",\n \"type\": \"number\",\n \"default\": \"2\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showActionButtons\",\n \"showResultMessage\",\n {\n \"key\": \"fieldsAlignment\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"row\",\n \"label\": \"Row (default)\"\n },\n {\n \"value\": \"column\",\n \"label\": \"Column\"\n }\n ]\n },\n \"fieldsInRow\"\n ]\n}",
+ "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"dataKeyType\": {\n \"title\": \"Datakey type\",\n \"type\": \"string\",\n \"default\": \"server\"\n },\n \"dataKeyValueType\": {\n \"title\": \"Datakey value type\",\n \"type\": \"string\",\n \"default\": \"string\"\n },\n \"required\": {\n \"title\": \"Value is required\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"isEditable\": {\n \"title\": \"Ability to edit attribute\",\n \"type\": \"string\",\n \"default\": \"editable\"\n },\n \"disabledOnDataKey\": {\n \"title\": \"Disable on false value of another datakey (specify datakey name)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"dataKeyHidden\": {\n \"title\": \"Hide input field\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"step\": {\n \"title\": \"Step interval between values (only for numbers)\",\n \"type\": \"number\",\n \"default\": \"1\"\n },\n \"requiredErrorMessage\": {\n \"title\": \"'Required' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"icon\": {\n \"title\": \"Icon to show before input cell\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"dataKeyType\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"server\",\n \"label\": \"Server attribute (default)\"\n },\n {\n \"value\": \"shared\",\n \"label\": \"Shared attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Timeseries\"\n }\n ]\n },\n {\n \"key\": \"dataKeyValueType\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"string\",\n \"label\": \"String\"\n },\n {\n \"value\": \"double\",\n \"label\": \"Double\"\n },\n {\n \"value\": \"integer\",\n \"label\": \"Integer\"\n },\n {\n \"value\": \"booleanCheckbox\",\n \"label\": \"Boolean (Checkbox)\"\n },\n {\n \"value\": \"booleanSwitch\",\n \"label\": \"Boolean (Switch)\"\n },\n {\n \"value\": \"dateTime\",\n \"label\": \"Date & Time\"\n },\n {\n \"value\": \"date\",\n \"label\": \"Date\"\n },\n {\n \"value\": \"time\",\n \"label\": \"Time\"\n }\n ]\n },\n \"required\",\n {\n \"key\": \"isEditable\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"editable\",\n \"label\": \"Editable (default)\"\n },\n {\n \"value\": \"disabled\",\n \"label\": \"Disabled\"\n },\n {\n \"value\": \"readonly\",\n \"label\": \"Read-only\"\n }\n ]\n },\n \"disabledOnDataKey\",\n \"dataKeyHidden\",\n \"step\",\n \"requiredErrorMessage\",\n\t\t{\n \t\t\"key\": \"icon\",\n\t\t\t\"type\": \"icon\"\n\t\t}\n ]\n}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update Multiple Attributes\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
},
diff --git a/ui/src/app/widget/lib/multiple-input-widget.js b/ui/src/app/widget/lib/multiple-input-widget.js
index ad8972ff7d..c68c22704e 100644
--- a/ui/src/app/widget/lib/multiple-input-widget.js
+++ b/ui/src/app/widget/lib/multiple-input-widget.js
@@ -41,25 +41,18 @@ function MultipleInputWidget() {
}
/*@ngInject*/
-function MultipleInputWidgetController($q, $scope, attributeService, toast, types, utils) {
+function MultipleInputWidgetController($q, $scope, $translate, attributeService, toast, types, utils) {
var vm = this;
- vm.dataKeyDetected = false;
- vm.hasAnyChange = false;
vm.entityDetected = false;
- vm.isValidParameter = true;
- vm.message = 'No entity selected';
-
- vm.rows = [];
- vm.rowIndex = 0;
+ vm.isAllParametersValid = true;
+ vm.data = [];
vm.datasources = null;
- vm.cellStyle = cellStyle;
- vm.textColor = textColor;
vm.discardAll = discardAll;
vm.inputChanged = inputChanged;
- vm.postData = postData;
+ vm.save = save;
$scope.$watch('vm.ctx', function() {
if (vm.ctx && vm.ctx.defaultSubscription) {
@@ -74,127 +67,109 @@ function MultipleInputWidgetController($q, $scope, attributeService, toast, type
$scope.$on('multiple-input-data-updated', function(event, formId) {
if (vm.formId == formId) {
- updateRowData(vm.subscription.data);
+ updateWidgetData(vm.subscription.data);
$scope.$digest();
}
});
- function defaultStyle() {
- return {};
- }
-
- function cellStyle(key, rowIndex, firstKey, lastKey) {
- var style = {};
- if (key) {
- var styleInfo = vm.stylesInfo[key.label];
- var value = key.currentValue;
- if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {
- try {
- style = styleInfo.cellStyleFunction(value);
- } catch (e) {
- style = {};
- }
- } else {
- style = defaultStyle();
- }
- }
- if (vm.settings.rowMargin) {
- if (angular.isUndefined(style.marginTop) && rowIndex != 0) {
- style.marginTop = (vm.settings.rowMargin / 2) + 'px';
- }
- if (angular.isUndefined(style.marginBottom)) {
- style.marginBottom = (vm.settings.rowMargin / 2) + 'px';
- }
- }
- if (vm.settings.columnMargin) {
- if (angular.isUndefined(style.marginLeft) && !firstKey) {
- style.marginLeft = (vm.settings.columnMargin / 2) + 'px';
- }
- if (angular.isUndefined(style.marginRight) && !lastKey) {
- style.marginRight = (vm.settings.columnMargin / 2) + 'px';
- }
- }
- return style;
- }
-
- function textColor(key) {
- var style = {};
- if (key) {
- var styleInfo = vm.stylesInfo[key.label];
- if (styleInfo.color) {
- style = { color: styleInfo.color };
- }
+ $scope.$on('multiple-input-resize', function(event, formId) {
+ if (vm.formId == formId) {
+ updateWidgetDisplaying();
}
- return style;
- }
+ });
function discardAll() {
- for (var r = 0; r < vm.rows.length; r++) {
- var row = vm.rows[r];
- for (var d = 0; d < row.data.length; d++ ) {
- row.data[d].currentValue = row.data[d].originalValue;
- }
+ for (var i = 0; i < vm.data.length; i++) {
+ vm.data[i].data.currentValue = vm.data[i].data.originalValue;
}
- vm.hasAnyChange = false;
+ $scope.multipleInputForm.$setPristine();
}
- function inputChanged() {
- var newValue = false;
- for (var r = 0; r < vm.rows.length; r++) {
- var row = vm.rows[r];
- for (var d = 0; d < row.data.length; d++ ) {
- if (!row.data[d].currentValue) {
- return;
- }
- if (row.data[d].currentValue !== row.data[d].originalValue) {
- newValue = true;
- }
+ function inputChanged(key) {
+ if (!vm.settings.showActionButtons) {
+ if (!key.settings.required || (key.settings.required && key.data && angular.isDefined(key.data.currentValue))) {
+ vm.save(key);
}
}
- vm.hasAnyChange = newValue;
}
- function postData() {
- var promises = [];
- for (var r = 0; r < vm.rows.length; r++) {
- var row = vm.rows[r];
- var datasource = row.datasource;
- var attributes = [];
- var newValues = false;
-
- for (var d = 0; d < row.data.length; d++ ) {
- if (row.data[d].currentValue !== row.data[d].originalValue) {
- attributes.push({
- key : row.data[d].name,
- value : row.data[d].currentValue,
- });
- newValues = true;
+ function save(key) {
+ var tasks = [];
+ var serverAttributes = [], sharedAttributes = [], telemetry = [];
+ var config = {
+ ignoreLoading: !vm.settings.showActionButtons
+ };
+ var data;
+ if (key) {
+ data = [key];
+ } else {
+ data = vm.data;
+ }
+ for (let i = 0; i < data.length; i++) {
+ var item = data[i];
+ if (item.data.currentValue !== item.data.originalValue) {
+ var attribute = {
+ key: item.name
+ };
+ switch (item.settings.dataKeyValueType) {
+ case 'dateTime':
+ case 'date':
+ attribute.value = item.data.currentValue.getTime();
+ break;
+ case 'time':
+ attribute.value = item.data.currentValue.getTime() - moment().startOf('day').valueOf();//eslint-disable-line
+ break;
+ default:
+ attribute.value = item.data.currentValue;
}
- }
- if (newValues) {
- promises.push(attributeService.saveEntityAttributes(
- datasource.entityType,
- datasource.entityId,
- vm.attributeScope,
- attributes));
+ switch (item.settings.dataKeyType) {
+ case 'shared':
+ sharedAttributes.push(attribute);
+ break;
+ case 'timeseries':
+ telemetry.push(attribute);
+ break;
+ default:
+ serverAttributes.push(attribute);
+ }
}
}
-
- if (promises.length) {
- $q.all(promises).then(
+ for (let i = 0; i < serverAttributes.length; i++) {
+ tasks.push(attributeService.saveEntityAttributes(
+ vm.datasources[0].entityType,
+ vm.datasources[0].entityId,
+ types.attributesScope.server.value,
+ serverAttributes,
+ config));
+ }
+ for (let i = 0; i < sharedAttributes.length; i++) {
+ tasks.push(attributeService.saveEntityAttributes(
+ vm.datasources[0].entityType,
+ vm.datasources[0].entityId,
+ types.attributesScope.shared.value,
+ sharedAttributes,
+ config));
+ }
+ for (let i = 0; i < telemetry.length; i++) {
+ tasks.push(attributeService.saveEntityTimeseries(
+ vm.datasources[0].entityType,
+ vm.datasources[0].entityId,
+ types.latestTelemetry.value,
+ telemetry,
+ config));
+ }
+ if (tasks.length) {
+ $q.all(tasks).then(
function success() {
- for (var d = 0; d < row.data.length; d++ ) {
- row.data[d].originalValue = row.data[d].currentValue;
- }
- vm.hasAnyChange = false;
+ $scope.multipleInputForm.$setPristine();
if (vm.settings.showResultMessage) {
- toast.showSuccess('Update successful', 1000, angular.element(vm.ctx.$container), 'bottom left');
+ toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(vm.ctx.$container), 'bottom left');
}
},
function fail() {
if (vm.settings.showResultMessage) {
- toast.showError('Update failed', angular.element(vm.ctx.$container), 'bottom left');
+ toast.showError($translate.instant('widgets.input-widgets.update-failed'), angular.element(vm.ctx.$container), 'bottom left');
}
}
);
@@ -211,78 +186,75 @@ function MultipleInputWidgetController($q, $scope, attributeService, toast, type
vm.ctx.widgetTitle = vm.widgetTitle;
- vm.attributeScope = vm.settings.attributesShared ? types.attributesScope.shared.value : types.attributesScope.server.value;
+ vm.isVerticalAlignment = !(vm.settings.fieldsAlignment === 'row');
+
+ if (!vm.isVerticalAlignment && vm.settings.fieldsInRow) {
+ vm.inputWidthSettings = 100 / vm.settings.fieldsInRow + '%';
+ }
}
function updateDatasources() {
+ if (vm.datasources && vm.datasources.length) {
+ var datasource = vm.datasources[0];
+ if (datasource.type === types.datasourceType.entity) {
+ for (var i = 0; i < datasource.dataKeys.length; i++) {
+ if ((datasource.entityType !== types.entityType.device) && (datasource.dataKeys[i].settings.dataKeyType !== 'server')) {
+ vm.isAllParametersValid = false;
+ }
+ vm.data.push(datasource.dataKeys[i]);
+ vm.data[i].data = {};
+ }
+ vm.entityDetected = true;
+ }
+ }
+ }
- vm.stylesInfo = {};
- vm.rows = [];
- vm.rowIndex = 0;
-
- if (vm.datasources) {
- vm.entityDetected = true;
- for (var ds = 0; ds < vm.datasources.length; ds++) {
- var row = {};
- var datasource = vm.datasources[ds];
- row.datasource = datasource;
- row.data = [];
- if (datasource.dataKeys) {
- vm.dataKeyDetected = true;
- for (var a = 0; a < datasource.dataKeys.length; a++ ) {
- var dataKey = datasource.dataKeys[a];
-
- if (dataKey.units) {
- dataKey.label += ' (' + dataKey.units + ')';
- }
-
- var keySettings = dataKey.settings;
- if (keySettings.inputTypeNumber) {
- keySettings.inputType = 'number';
- } else {
- keySettings.inputType = 'text';
- }
+ function updateWidgetData(data) {
+ for (var i = 0; i < vm.data.length; i++) {
+ var keyData = data[i].data;
+ if (keyData && keyData.length) {
+ var value;
+ switch (vm.data[i].settings.dataKeyValueType) {
+ case 'dateTime':
+ case 'date':
+ value = moment(keyData[0][1]).toDate(); // eslint-disable-line
+ break;
+ case 'time':
+ value = moment().startOf('day').add(keyData[0][1], 'ms').toDate(); // eslint-disable-line
+ break;
+ case 'booleanCheckbox':
+ case 'booleanSwitch':
+ value = (keyData[0][1] === 'true');
+ break;
+ default:
+ value = keyData[0][1];
+ }
- var cellStyleFunction = null;
- var useCellStyleFunction = false;
+ vm.data[i].data = {
+ currentValue: value,
+ originalValue: value
+ };
- if (keySettings.useCellStyleFunction === true) {
- if (angular.isDefined(keySettings.cellStyleFunction) && keySettings.cellStyleFunction.length > 0) {
- try {
- cellStyleFunction = new Function('value', keySettings.cellStyleFunction);
- useCellStyleFunction = true;
- } catch (e) {
- cellStyleFunction = null;
- useCellStyleFunction = false;
- }
+ if (vm.data[i].settings.isEditable === 'editable' && vm.data[i].settings.disabledOnDataKey) {
+ var conditions = data.filter((item) => {
+ return item.dataKey.name === vm.data[i].settings.disabledOnDataKey;
+ });
+ if (conditions && conditions.length) {
+ if (conditions[0].data.length) {
+ if (conditions[0].data[0][1] === 'false') {
+ vm.data[i].settings.disabledOnCondition = true;
+ } else {
+ vm.data[i].settings.disabledOnCondition = !conditions[0].data[0][1];
}
}
-
- vm.stylesInfo[dataKey.label] = {
- useCellStyleFunction: useCellStyleFunction,
- cellStyleFunction: cellStyleFunction,
- color: keySettings.color
- };
-
- row.data.push(dataKey);
}
- vm.rows.push(row);
}
}
}
}
- function updateRowData(data) {
- var dataIndex = 0;
- for (var r = 0; r < vm.rows.length; r++) {
- var row = vm.rows[r];
- for (var d = 0; d < row.data.length; d++ ) {
- var keyData = data[dataIndex++].data;
- if (keyData && keyData.length && keyData[0].length > 1) {
- row.data[d].currentValue = row.data[d].originalValue = keyData[0][1];
- }
- }
- }
+ function updateWidgetDisplaying() {
+ vm.changeAlignment = (vm.ctx.$container[0].offsetWidth < 620);
+ vm.smallWidthContainer = (vm.ctx.$container[0].offsetWidth < 420);
}
-
}
diff --git a/ui/src/app/widget/lib/multiple-input-widget.scss b/ui/src/app/widget/lib/multiple-input-widget.scss
index 7e16b3f967..ea5cf019fc 100644
--- a/ui/src/app/widget/lib/multiple-input-widget.scss
+++ b/ui/src/app/widget/lib/multiple-input-widget.scss
@@ -15,15 +15,25 @@
*/
.tb-multiple-input {
height: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
- .md-button.md-icon-button {
- width: 32px;
- min-width: 32px;
- height: 32px;
- min-height: 32px;
- padding: 0 !important;
- margin: 0;
- line-height: 20px;
+ .input-field {
+ padding-right: 10px;
+
+ md-input-container,
+ md-checkbox,
+ md-switch {
+ margin-bottom: 5px;
+ }
+ }
+
+ md-checkbox {
+ margin-top: 22px;
+ }
+
+ md-switch {
+ margin-top: 20px;
}
.md-icon-button md-icon {
@@ -32,10 +42,56 @@
height: 20px;
min-height: 20px;
font-size: 20px;
+ }
+
+ .date-time-input {
+ &__label {
+ margin-left: 36px;
+ font-size: 12px;
+ color: rgba(0, 0, 0, .54);
+ }
+
+ mdp-date-picker,
+ mdp-time-picker {
+ width: 100%;
+
+ .md-button.md-icon-button {
+ margin: 5px 0 0;
+ }
- &:not([disabled]) {
- color: #f66;
+ md-input-container {
+ width: 100%;
+ margin: 2px 0;
+
+ label {
+ display: none;
+ }
+ }
+ }
+ }
+
+ .vertical-alignment {
+ flex-direction: column;
+
+ md-checkbox,
+ md-switch {
+ margin-top: 18px;
}
+
+ md-switch {
+ display: flex;
+ justify-content: space-between;
+ }
+
+ .date-time-input {
+ &__label {
+ margin-top: 10px;
+ }
+ }
+ }
+
+ .vertically-aligned {
+ flex-direction: column;
}
}
diff --git a/ui/src/app/widget/lib/multiple-input-widget.tpl.html b/ui/src/app/widget/lib/multiple-input-widget.tpl.html
index 1263919825..145b90a32d 100644
--- a/ui/src/app/widget/lib/multiple-input-widget.tpl.html
+++ b/ui/src/app/widget/lib/multiple-input-widget.tpl.html
@@ -15,53 +15,134 @@
limitations under the License.
-->
-