9 changed files with 397 additions and 7 deletions
@ -0,0 +1,259 @@ |
|||
/* |
|||
* Copyright © 2016-2019 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
import './multiple-input-widget.scss'; |
|||
|
|||
/* eslint-disable import/no-unresolved, import/default */ |
|||
|
|||
import multipleInputWidgetTemplate from './multiple-input-widget.tpl.html'; |
|||
|
|||
/* eslint-enable import/no-unresolved, import/default */ |
|||
|
|||
export default angular.module('thingsboard.widgets.multipleInputWidget', []) |
|||
.directive('tbMultipleInputWidget', MultipleInputWidget) |
|||
.name; |
|||
|
|||
/*@ngInject*/ |
|||
function MultipleInputWidget() { |
|||
return { |
|||
restrict: "E", |
|||
scope: true, |
|||
bindToController: { |
|||
formId: '=', |
|||
ctx: '=' |
|||
}, |
|||
controller: MultipleInputWidgetController, |
|||
controllerAs: 'vm', |
|||
templateUrl: multipleInputWidgetTemplate |
|||
}; |
|||
} |
|||
|
|||
/*@ngInject*/ |
|||
function MultipleInputWidgetController($q, $scope, 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.datasources = null; |
|||
|
|||
vm.cellStyle = cellStyle; |
|||
vm.discardAll = discardAll; |
|||
vm.inputChanged = inputChanged; |
|||
vm.postData = postData; |
|||
|
|||
$scope.$watch('vm.ctx', function() { |
|||
if (vm.ctx && vm.ctx.defaultSubscription) { |
|||
vm.settings = vm.ctx.settings; |
|||
vm.widgetConfig = vm.ctx.widgetConfig; |
|||
vm.subscription = vm.ctx.defaultSubscription; |
|||
vm.datasources = vm.subscription.datasources; |
|||
initializeConfig(); |
|||
updateDatasources(); |
|||
} |
|||
}); |
|||
|
|||
$scope.$on('multiple-input-data-updated', function(event, formId) { |
|||
if (vm.formId == formId) { |
|||
updateRowData(vm.subscription.data); |
|||
$scope.$digest(); |
|||
} |
|||
}); |
|||
|
|||
function defaultStyle() { |
|||
return {}; |
|||
} |
|||
|
|||
function cellStyle(key) { |
|||
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(); |
|||
} |
|||
} |
|||
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; |
|||
} |
|||
} |
|||
vm.hasAnyChange = false; |
|||
} |
|||
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
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; |
|||
} |
|||
} |
|||
|
|||
if (newValues) { |
|||
promises.push(attributeService.saveEntityAttributes( |
|||
datasource.entityType, |
|||
datasource.entityId, |
|||
vm.attributeScope, |
|||
attributes)); |
|||
} |
|||
} |
|||
|
|||
if (promises.length) { |
|||
$q.all(promises).then( |
|||
function success() { |
|||
for (var d = 0; d < row.data.length; d++ ) { |
|||
row.data[d].originalValue = row.data[d].currentValue; |
|||
} |
|||
vm.hasAnyChange = false; |
|||
if (vm.settings.showResultMessage) { |
|||
toast.showSuccess('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'); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
function initializeConfig() { |
|||
|
|||
if (vm.settings.widgetTitle && vm.settings.widgetTitle.length) { |
|||
vm.widgetTitle = utils.customTranslation(vm.settings.widgetTitle, vm.settings.widgetTitle); |
|||
} else { |
|||
vm.widgetTitle = vm.ctx.widgetConfig.title; |
|||
} |
|||
|
|||
vm.ctx.widgetTitle = vm.widgetTitle; |
|||
|
|||
vm.attributeScope = vm.settings.attributesShared ? types.attributesScope.shared.value : types.attributesScope.server.value; |
|||
} |
|||
|
|||
function updateDatasources() { |
|||
|
|||
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'; |
|||
} |
|||
|
|||
var cellStyleFunction = null; |
|||
var useCellStyleFunction = false; |
|||
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
|
|||
vm.stylesInfo[dataKey.label] = { |
|||
useCellStyleFunction: useCellStyleFunction, |
|||
cellStyleFunction: cellStyleFunction |
|||
}; |
|||
|
|||
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]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
/** |
|||
* Copyright © 2016-2019 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
.tb-multiple-input { |
|||
height: 100%; |
|||
|
|||
.md-button.md-icon-button { |
|||
width: 32px; |
|||
min-width: 32px; |
|||
height: 32px; |
|||
min-height: 32px; |
|||
padding: 0 !important; |
|||
margin: 0; |
|||
line-height: 20px; |
|||
} |
|||
|
|||
.md-icon-button md-icon { |
|||
width: 20px; |
|||
min-width: 20px; |
|||
height: 20px; |
|||
min-height: 20px; |
|||
font-size: 20px; |
|||
|
|||
&:not([disabled]) { |
|||
color: #f66; |
|||
} |
|||
} |
|||
} |
|||
|
|||
md-toast { |
|||
min-width: 0; |
|||
|
|||
.md-toast-content { |
|||
font-size: 14px !important; |
|||
} |
|||
} |
|||
|
|||
.footer { |
|||
position: absolute; |
|||
bottom: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2019 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<form class="tb-multiple-input" name="multipleInputForm" ng-submit="vm.postData($event)"> |
|||
<div style="padding: 0 8px; margin: auto 0;"> |
|||
<div ng-show="vm.entityDetected" layout="row" flex ng-repeat="row in vm.rows track by $index"> |
|||
<div layout="column" flex ng-repeat="key in row.data track by $index"> |
|||
<md-input-container class="md-icon-float" ng-style="vm.cellStyle(key)"> |
|||
<label>{{key.label}}</label> |
|||
<md-icon class="material-icons" ng-if="key.settings.icon"> |
|||
{{key.settings.icon}} |
|||
</md-icon> |
|||
<input name="key.name" |
|||
ng-model="key.currentValue" |
|||
type="{{key.settings.inputType}}" |
|||
step="{{key.settings.step}}" |
|||
md-select-on-focus |
|||
ng-change="vm.inputChanged()"> |
|||
</md-input-container> |
|||
</div> |
|||
</div> |
|||
|
|||
<div style="text-align: center; font-size: 18px; color: #a0a0a0;" ng-hide="vm.entityDetected" ng-bind="vm.message" |
|||
></div> |
|||
<div style="text-align: center; font-size: 18px; color: #a0a0a0;" ng-show="vm.entityDetected && !vm.dataKeyDetected"> |
|||
No attribute is selected |
|||
</div> |
|||
<div style="text-align: center; font-size: 18px; color: #a0a0a0;" ng-show="vm.entityDetected && !vm.isValidParameter"> |
|||
Timeseries parameter cannot be used in this widget |
|||
</div> |
|||
</div> |
|||
<div class="footer md-padding" layout="row" layout-align="end center" ng-show="vm.entityDetected && vm.dataKeyDetected"> |
|||
<md-button class="md-primary" ng-click="vm.discardAll()" style="max-height: 50px;margin-right:20px;" ng-disabled="!vm.hasAnyChange"> |
|||
{{ 'action.discard-changes' | translate }} |
|||
</md-button> |
|||
<md-button class="md-raised md-primary" type="submit" value="Submit" style="max-height: 50px;margin-right:20px;" |
|||
ng-disabled="!vm.hasAnyChange" ng-click="vm.isFocused = false"> |
|||
{{ 'action.save' | translate }} |
|||
</md-button> |
|||
</div> |
|||
</form> |
|||
Loading…
Reference in new issue